当我使用ThreadStatic Attribute探索线程和任务时,我刚刚遇到了一些奇怪的事情。我相信这可能非常特定于线程和任务。 请考虑以下代码段:
[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
我有多少线程,我无法真正看到'范围'值被初始化并显示为10.这里初始化范围变量的位置以及为什么在初始化静态变量时线程和任务之间存在区别?
我错过了一些基本的东西吗?提前致谢。
答案 0 :(得分:3)
您的[ThreadStatic]由包含此代码的类的静态构造函数初始化。通过创建类实例的线程或使用静态成员,以先到者为准。因此,根据定义,您创建的两个 new 线程永远不会看到初始值。
奇怪的行为实际上是在第一个片段中。你没有指望的是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]。
答案 1 :(得分:1)
在第一种情况下,def parse(self,response):
if response.status == 404:
#your action on error
变量已被初始化类的线程(运行静态构造函数的线程)初始化为range
。在所有其他线程上它将等于10
。然后从同一个帖子中调用0
。
Parallel.Invoke
不会生成两个线程,而是调用当前线程上的第一个操作,而调用线程池Parallel.Invoke
上的第二个操作。由于它在所有操作完成后返回,因此不需要使用3个线程,其中一个被阻塞,等待其他2个完成。
您可以找到relevant code in the reference source:
Task
你的第二个片段产生2个线程,因此不会从初始线程执行// 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);
。在这种情况下,您永远不会看到Console.WriteLine
值。