线程与任务中的 ThreadStatic 属性
ThreadStatic Atttribute in Thread vs Task
我在使用 ThreadStatic 属性探索线程和任务时遇到了一些奇怪的事情。我相信这可能非常特定于线程和任务。
考虑以下代码片段:
[ThreadStatic]
static int range=10;
Action action = () =>
{Console.WriteLine("Thread = {0}, Value = {1}", Thread.CurrentThread.ManagedThreadId, range);
Parallel.Invoke( action, action);
这给出了输出:
Thread = 2, Value = 10
Thread = 3, Value = 0
这绝对没问题,因为 ThreadStatic 变量只能初始化一次,因此,第二行显示为 0。
但是,请考虑以下情况:
[ThreadStatic]
static int range=10;
new Thread(() =>
{
Console.WriteLine("Thread = {0}, Value = {1}" Thread.CurrentThread.ManagedThreadId, range);
}).Start();
new Thread(() =>
{
Console.WriteLine("Thread = {0}, Value = {1}" Thread.CurrentThread.ManagedThreadId, range);
}).Start();
这一行给出了输出:
Thread = 6, Value = 0
Thread = 7, Value = 0
我跨越了多少线程,我无法真正看到 'range' 值被初始化并显示为 10。这里初始化的范围变量在哪里以及为什么线程和任务之间存在区别初始化静态变量?
我是不是漏掉了一些基本的东西?提前致谢。
在第一种情况下,range
变量已被初始化 class 的线程初始化为 10
(运行 静态构造函数) .在所有其他线程上它将等于 0
。然后您从同一个线程调用 Parallel.Invoke
。
Parallel.Invoke
不会生成两个线程,而是会在 当前 线程上调用第一个操作,在线程池 Task
上调用第二个操作。因为它 returns 一旦完成所有操作,就不需要为此使用 3 个线程,其中一个被阻塞等待其他 2 个完成。
您可以找到 relevant code in the reference source:
// Optimization: Use current thread to run something before we block waiting for all tasks.
tasks[0] = new Task(actionsCopy[0]);
tasks[0].RunSynchronously(parallelOptions.EffectiveTaskScheduler);
您的第二个代码段产生了 2 个线程,因此 Console.WriteLine
永远不会从初始线程执行。在这种情况下,您永远不会看到 10
值。
您的 [ThreadStatic] 由包含此代码的 class 的静态构造函数初始化。通过创建 class 实例或使用静态成员的线程,以先到者为准。所以根据定义,你创建的两个new线程是永远看不到初始值的。
古怪的行为实际上在 first 片段中。您没有想到的是 Parallel.Invoke() 还使用调用 Invoke() 的线程来完成部分工作。所以它实际上可以看到初始值。稍微重写一下代码可以向您展示:
class Test {
[ThreadStatic]
static int range=10;
public static void Run() {
Action action = () => {
Console.WriteLine("Thread = {0}, Value = {1}", Thread.CurrentThread.ManagedThreadId, range);
};
Console.WriteLine("Start thread = {0}, Value = {1}", Thread.CurrentThread.ManagedThreadId, range);
Parallel.Invoke(action, action);
}
}
输出:
Start thread = 8, Value = 10
Thread = 8, Value = 10
Thread = 9, Value = 0
当然不是真正的问题,您不能在并行代码中使用 [ThreadStatic]。
我在使用 ThreadStatic 属性探索线程和任务时遇到了一些奇怪的事情。我相信这可能非常特定于线程和任务。 考虑以下代码片段:
[ThreadStatic]
static int range=10;
Action action = () =>
{Console.WriteLine("Thread = {0}, Value = {1}", Thread.CurrentThread.ManagedThreadId, range);
Parallel.Invoke( action, action);
这给出了输出:
Thread = 2, Value = 10
Thread = 3, Value = 0
这绝对没问题,因为 ThreadStatic 变量只能初始化一次,因此,第二行显示为 0。
但是,请考虑以下情况:
[ThreadStatic]
static int range=10;
new Thread(() =>
{
Console.WriteLine("Thread = {0}, Value = {1}" Thread.CurrentThread.ManagedThreadId, range);
}).Start();
new Thread(() =>
{
Console.WriteLine("Thread = {0}, Value = {1}" Thread.CurrentThread.ManagedThreadId, range);
}).Start();
这一行给出了输出:
Thread = 6, Value = 0
Thread = 7, Value = 0
我跨越了多少线程,我无法真正看到 'range' 值被初始化并显示为 10。这里初始化的范围变量在哪里以及为什么线程和任务之间存在区别初始化静态变量?
我是不是漏掉了一些基本的东西?提前致谢。
在第一种情况下,range
变量已被初始化 class 的线程初始化为 10
(运行 静态构造函数) .在所有其他线程上它将等于 0
。然后您从同一个线程调用 Parallel.Invoke
。
Parallel.Invoke
不会生成两个线程,而是会在 当前 线程上调用第一个操作,在线程池 Task
上调用第二个操作。因为它 returns 一旦完成所有操作,就不需要为此使用 3 个线程,其中一个被阻塞等待其他 2 个完成。
您可以找到 relevant code in the reference source:
// Optimization: Use current thread to run something before we block waiting for all tasks.
tasks[0] = new Task(actionsCopy[0]);
tasks[0].RunSynchronously(parallelOptions.EffectiveTaskScheduler);
您的第二个代码段产生了 2 个线程,因此 Console.WriteLine
永远不会从初始线程执行。在这种情况下,您永远不会看到 10
值。
您的 [ThreadStatic] 由包含此代码的 class 的静态构造函数初始化。通过创建 class 实例或使用静态成员的线程,以先到者为准。所以根据定义,你创建的两个new线程是永远看不到初始值的。
古怪的行为实际上在 first 片段中。您没有想到的是 Parallel.Invoke() 还使用调用 Invoke() 的线程来完成部分工作。所以它实际上可以看到初始值。稍微重写一下代码可以向您展示:
class Test {
[ThreadStatic]
static int range=10;
public static void Run() {
Action action = () => {
Console.WriteLine("Thread = {0}, Value = {1}", Thread.CurrentThread.ManagedThreadId, range);
};
Console.WriteLine("Start thread = {0}, Value = {1}", Thread.CurrentThread.ManagedThreadId, range);
Parallel.Invoke(action, action);
}
}
输出:
Start thread = 8, Value = 10
Thread = 8, Value = 10
Thread = 9, Value = 0
当然不是真正的问题,您不能在并行代码中使用 [ThreadStatic]。