.net CallContext.SetData()-当线程处于活动状态(TPL)时,对象是否可用?
各位 假设我使用线程10、11、12中的CallContext.SetData()存储对象Car的三个新实例。这些线程完成执行。然后我执行另一个多线程操作(可能与第一个操作不同),它使用线程10、11、12。GetData()是否会检索与我存储的三个对象相同的对象?或者是现在的环境不同了,那些物体消失了 我的特定用例是任务并行库。我正在使用TPL并行化一些操作,我想了解TPL调用之间通过CallContext.SetData()存储的数据会发生什么情况 编辑.net CallContext.SetData()-当线程处于活动状态(TPL)时,对象是否可用?,.net,.net-3.5,.net-4.0,task-parallel-library,.net,.net 3.5,.net 4.0,Task Parallel Library,各位 假设我使用线程10、11、12中的CallContext.SetData()存储对象Car的三个新实例。这些线程完成执行。然后我执行另一个多线程操作(可能与第一个操作不同),它使用线程10、11、12。GetData()是否会检索与我存储的三个对象相同的对象?或者是现在的环境不同了,那些物体消失了 我的特定用例是任务并行库。我正在使用TPL并行化一些操作,我想了解TPL调用之间通过CallContext.SetData()存储的数据会发生什么情况 编辑 根据@wageoghe的建议,我尝试
根据@wageoghe的建议,我尝试了ThreadLocal,效果很好 更新代码以证明这一点:
using System;
using System.Threading;
using System.Threading.Tasks;
namespace TlsTest
{
public class Program
{
public static void Main()
{
Console.WriteLine( "-------using threadpool---------" );
UseThreadPool();
Console.WriteLine( "-------using tasks---------" );
UseTasks();
Console.WriteLine( "-------using parallel for---------" );
UseParallelFor();
Console.ReadKey();
}
public static void UseThreadPool()
{
var finish = new CountdownEvent( TotalThreads );
for ( int i = 0 ; i < TotalThreads ; i++ )
{
ThreadPool.QueueUserWorkItem( x =>
{
int id = Thread.CurrentThread.ManagedThreadId;
Thread.Sleep( SleepMilliseconds );
if ( ThreadId.IsValueCreated )
{
Console.WriteLine( "thread [{0}], tls.thread [{1}] - value already in Tls" , id , ThreadId.Value );
}
else
{
Console.WriteLine( "thread [{0}] - no Tls value" , id );
ThreadId.Value = id;
}
Thread.Sleep( SleepMilliseconds );
finish.Signal();
} );
}
finish.Wait();
}
public static void UseTasks()
{
const TaskCreationOptions taskCreationOpt = TaskCreationOptions.None;
var allTasks = new Task[ TotalThreads ];
for ( int i = 0 ; i < TotalThreads ; i++ )
{
Task task = Task.Factory.StartNew( () =>
{
int id = Thread.CurrentThread.ManagedThreadId;
Thread.Sleep( SleepMilliseconds );
if ( ThreadId.IsValueCreated )
{
Console.WriteLine( "thread [{0}], tls.thread [{1}] - value already in Tls" , id , ThreadId.Value );
}
else
{
Console.WriteLine( "thread [{0}] - no Tls value" , id );
ThreadId.Value = id;
}
Thread.Sleep( SleepMilliseconds );
} , taskCreationOpt );
allTasks[ i ] = task;
}
Task.WaitAll( allTasks );
}
public static void UseParallelFor()
{
var options = new ParallelOptions();
options.MaxDegreeOfParallelism = 8;
Parallel.For( 0 , TotalThreads , options , i =>
{
int id = Thread.CurrentThread.ManagedThreadId;
Thread.Sleep( SleepMilliseconds );
if ( ThreadId.IsValueCreated )
{
Console.WriteLine( "thread [{0}], tls.thread [{1}] - value already in Tls" , id , ThreadId.Value );
}
else
{
Console.WriteLine( "thread [{0}] - no Tls value" , id );
ThreadId.Value = id;
}
Thread.Sleep( SleepMilliseconds );
} );
}
private static readonly ThreadLocal<int> ThreadId = new ThreadLocal<int>();
private const int TotalThreads = 100;
private const int SleepMilliseconds = 500;
}
}
使用系统;
使用系统线程;
使用System.Threading.Tasks;
名称空间TlsTest
{
公共课程
{
公共静态void Main()
{
Console.WriteLine(“----使用线程池----”;
使用线程池();
Console.WriteLine(“----使用任务---------”);
使用任务();
Console.WriteLine(“----使用parallel表示----”;
UseParallelFor();
Console.ReadKey();
}
公共静态void UseThreadPool()
{
var finish=新的倒计时事件(TotalThreads);
对于(inti=0;i
{
int id=Thread.CurrentThread.ManagedThreadId;
睡眠(睡眠毫秒);
if(ThreadId.IsValueCreated)
{
WriteLine(“thread[{0}],tls.thread[{1}]-值已在tls中”,id,ThreadId.value);
}
其他的
{
WriteLine(“线程[{0}]-没有Tls值”,id);
ThreadId.Value=id;
}
睡眠(睡眠毫秒);
完成。信号();
} );
}
完成。等待();
}
公共静态任务()
{
const TaskCreationOptions taskCreationOpt=TaskCreationOptions.None;
var allTasks=新任务[TotalThreads];
对于(inti=0;i
{
int id=Thread.CurrentThread.ManagedThreadId;
睡眠(睡眠毫秒);
if(ThreadId.IsValueCreated)
{
WriteLine(“thread[{0}],tls.thread[{1}]-值已在tls中”,id,ThreadId.value);
}
其他的
{
WriteLine(“线程[{0}]-没有Tls值”,id);
ThreadId.Value=id;
}
睡眠(睡眠毫秒);
},taskCreationOpt);
所有任务[i]=任务;
}
Task.WaitAll(所有任务);
}
用于()的公共静态void
{
var options=新的ParallelOptions();
options.MaxDegreeOfParallelism=8;
对于(0,TotalThreads,options,i=>
{
int id=Thread.CurrentThread.ManagedThreadId;
睡眠(睡眠毫秒);
if(ThreadId.IsValueCreated)
{
WriteLine(“thread[{0}],tls.thread[{1}]-值已在tls中”,id,ThreadId.value);
}
其他的
{
WriteLine(“线程[{0}]-没有Tls值”,id);
ThreadId.Value=id;
}
睡眠(睡眠毫秒);
} );
}
private static readonly ThreadLocal ThreadId=new ThreadLocal();
private const int TotalThreads=100;
私有常量int=500;
}
}
[更新]
事实上,我最初的答案(在这篇文章的底部)似乎有一部分是错的
我编写了一个小测试程序,用于测试在CallContext中存储来自线程和任务、线程池线程以及并行线程的数据的场景。在Tasks测试和ThreadPool测试中,当重用同一线程(由ManagedThreadId确定)时,不会再次看到存储在CallContext中的数据。但是,对于Parallel.For,当重用同一线程(由ManagedThreadId确定)时,会再次看到存储在CallContext中的数据。我觉得这很有趣。我不确定这些结果是否符合预期,或者我的程序是否有问题
要尝试每种情况,只需取消注释所需的测试函数
您将看到,任务和线程池线程在并行时的后续线程重用中从未遇到CallContext数据
Parallel.For的行为似乎不一致。当我运行Parallel.For case时,我可以看到一个给定的线程在重用该线程时并不一定总能找到CallContext数据。例如,以下是一次程序运行的输出(UseParallelforUn注释):
线程[9]-无CallContext值
线程[10]-无CallContext值
线程[11]-无CallContext值
线程[12]-无CallContext值
线程[9],cc.thread[9]-值已在CallContext中
{
int id=Thread.CurrentThread。
thread [9] - no CallContext value
thread [10] - no CallContext value
thread [11] - no CallContext value
thread [12] - no CallContext value
thread [9], cc.thread [9] - value already in CallContext <-- this is expected as this is the main thread
thread [10] - no CallContext value
thread [13] - no CallContext value
thread [11] - no CallContext value
thread [12] - no CallContext value
thread [14] - no CallContext value
thread [9], cc.thread [9] - value already in CallContext
thread [10], cc.thread [10] - value already in CallContext
thread [11], cc.thread [11] - value already in CallContext
thread [13] - no CallContext value
thread [15] - no CallContext value
thread [12], cc.thread [12] - value already in CallContext
thread [16] - no CallContext value
thread [14] - no CallContext value
thread [9], cc.thread [9] - value already in CallContext
thread [10] - no CallContext value
thread [17] - no CallContext value
thread [13], cc.thread [13] - value already in CallContext
thread [15] - no CallContext value
thread [11] - no CallContext value
thread [12] - no CallContext value
thread [14], cc.thread [14] - value already in CallContext
thread [18] - no CallContext value
thread [16] - no CallContext value
thread [9], cc.thread [9] - value already in CallContext
thread [10], cc.thread [10] - value already in CallContext
thread [13] - no CallContext value
thread [15], cc.thread [15] - value already in CallContext
thread [11], cc.thread [11] - value already in CallContext
thread [17] - no CallContext value
thread [19] - no CallContext value
thread [18] - no CallContext value
thread [16], cc.thread [16] - value already in CallContext
thread [14] - no CallContext value
thread [20] - no CallContext value
thread [12], cc.thread [12] - value already in CallContext
thread [9], cc.thread [9] - value already in CallContext
thread [10], cc.thread [10] - value already in CallContext
thread [21] - no CallContext value
thread [15] - no CallContext value
thread [11], cc.thread [11] - value already in CallContext
thread [17], cc.thread [17] - value already in CallContext
thread [13], cc.thread [13] - value already in CallContext
thread [19] - no CallContext value
thread [22] - no CallContext value
thread [18], cc.thread [18] - value already in CallContext
thread [16] - no CallContext value
thread [20] - no CallContext value
thread [14], cc.thread [14] - value already in CallContext
thread [12], cc.thread [12] - value already in CallContext
thread [9], cc.thread [9] - value already in CallContext
thread [10], cc.thread [10] - value already in CallContext
thread [23] - no CallContext value
thread [15], cc.thread [15] - value already in CallContext
thread [21] - no CallContext value
thread [11], cc.thread [11] - value already in CallContext
thread [17] - no CallContext value
thread [13], cc.thread [13] - value already in CallContext
thread [19], cc.thread [19] - value already in CallContext
thread [22] - no CallContext value
thread [16], cc.thread [16] - value already in CallContext
thread [18] - no CallContext value
thread [24] - no CallContext value
thread [20], cc.thread [20] - value already in CallContext
thread [14], cc.thread [14] - value already in CallContext
thread [12], cc.thread [12] - value already in CallContext
thread [9], cc.thread [9] - value already in CallContext
thread [10] - no CallContext value
thread [15], cc.thread [15] - value already in CallContext
thread [21], cc.thread [21] - value already in CallContext
thread [17], cc.thread [17] - value already in CallContext
thread [13], cc.thread [13] - value already in CallContext
thread [22], cc.thread [22] - value already in CallContext
thread [18], cc.thread [18] - value already in CallContext
thread [16], cc.thread [16] - value already in CallContext
thread [14], cc.thread [14] - value already in CallContext
thread [9], cc.thread [9] - value already in CallContext
thread [10], cc.thread [10] - value already in CallContext
thread [15], cc.thread [15] - value already in CallContext
thread [17], cc.thread [17] - value already in CallContext
thread [18], cc.thread [18] - value already in CallContext
thread [16], cc.thread [16] - value already in CallContext
thread [9], cc.thread [9] - value already in CallContext
thread [10], cc.thread [10] - value already in CallContext
thread [17], cc.thread [17] - value already in CallContext
thread [18], cc.thread [18] - value already in CallContext
thread [9], cc.thread [9] - value already in CallContext
thread [10], cc.thread [10] - value already in CallContext
thread [9], cc.thread [9] - value already in CallContext
thread [10], cc.thread [10] - value already in CallContext
thread [9], cc.thread [9] - value already in CallContext
thread [10], cc.thread [10] - value already in CallContext
thread [9], cc.thread [9] - value already in CallContext
thread [10], cc.thread [10] - value already in CallContext
thread [9], cc.thread [9] - value already in CallContext
thread [10], cc.thread [10] - value already in CallContext
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading;
using System.Threading.Tasks;
using System.Runtime.Remoting.Messaging;
namespace CallContextTest
{
class Program
{
static void Main(string[] args)
{
//UseTasks();
//UseThreadPool();
UseParallelFor();
Console.ReadKey();
}
public static void UseThreadPool()
{
int totalThreads = 100;
CountdownEvent finish = new CountdownEvent(totalThreads);
for (int i = 0; i < totalThreads; i++)
{
int ii = i;
ThreadPool.QueueUserWorkItem(x =>
{
int id = Thread.CurrentThread.ManagedThreadId;
Thread.Sleep(1000);
object o = CallContext.GetData("threadid");
if (o == null)
{
//Always gets here.
Console.WriteLine("thread [{0}] - no CallContext value", id);
CallContext.SetData("threadid", id);
}
else
{
//Never gets here.
Console.WriteLine("thread [{0}], cc.thread [{1}] - value already in CallContext", o, id);
}
Thread.Sleep(1000);
finish.Signal();
});
}
finish.Wait();
}
public static void UseTasks()
{
int totalThreads = 100;
TaskCreationOptions taskCreationOpt = TaskCreationOptions.None;
Task task = null;
Task[] allTasks = new Task[totalThreads];
for (int i = 0; i < totalThreads; i++)
{
int ii = i;
task = Task.Factory.StartNew(() =>
{
int id = Thread.CurrentThread.ManagedThreadId;
Thread.Sleep(1000);
object o = CallContext.GetData("threadid");
if (o == null)
{
//Always gets here.
Console.WriteLine("thread [{0}] - no CallContext value", id);
CallContext.SetData("threadid", id);
}
else
{
//Never gets here.
Console.WriteLine("thread [{0}], cc.thread [{1}] - value already in CallContext", o, id);
}
Thread.Sleep(1000);
}, taskCreationOpt);
allTasks[i] = task;
}
Task.WaitAll(allTasks);
}
public static void UseParallelFor()
{
int totalThreads = 100;
Parallel.For(0, totalThreads, i =>
{
int ii = i;
int id = Thread.CurrentThread.ManagedThreadId;
Thread.Sleep(1000);
object o = CallContext.GetData("threadid");
if (o == null)
{
//Sometimes gets here.
Console.WriteLine("thread [{0}] - no CallContext value", id);
CallContext.SetData("threadid", id);
}
else
{
//Sometimes gets here as threads are reused.
Console.WriteLine("thread [{0}], cc.thread [{1}] - value already in CallContext", o, id);
}
Thread.Sleep(1000);
});
}
}
}