在使用任务并行库之前,我经常使用CorrelationManager.ActivityId跟踪多个线程的跟踪/错误报告.
ActivityId存储在Thread Local Storage中,所以每个线程都有自己的副本.这个想法是当您启动一个线程(活动)时,您将分配一个新的ActivityId. ActivityId将被写入到具有任何其他跟踪信息的日志中,从而可以为单个“活动”列出跟踪信息.这对于WCF非常有用,因为ActivityId可以转移到服务组件.
以下是我正在谈论的例子:
static void Main(string[] args) { ThreadPool.QueueUserWorkItem(new WaitCallback((o) => { DoWork(); })); } static void DoWork() { try { Trace.CorrelationManager.ActivityId = Guid.NewGuid(); //The functions below contain tracing which logs the ActivityID. CallFunction1(); CallFunction2(); CallFunction3(); } catch (Exception ex) { Trace.Write(Trace.CorrelationManager.ActivityId + " " + ex.ToString()); } }
现在,使用TPL,我的理解是,多个任务共享线程.这是否意味着ActivityId容易被重新初始化中间任务(由另一个任务)?是否有新的机制来处理活动追踪?
解决方法
我进行了一些实验,结果是我的问题的假设是不正确的 – 使用TPL创建的多个任务不会同时在同一个线程上运行.
ThreadLocalStorage可以安全地使用.NET 4.0中的TPL,因为一次只能由一个任务使用一个线程.
任务可以同时共享线程的假设是基于我在DotNetRocks上听到的关于c#5.0的访问(对不起,我不记得那是哪个节目) – 所以我的问题可能(或可能不会)变得相关.
我的实验开始了许多任务,并记录了运行了多少任务,花费了多少时间以及消耗了多少线程.如果有人想重复,代码如下.
class Program { static void Main(string[] args) { int totalThreads = 100; TaskCreationOptions taskCreationOpt = TaskCreationOptions.None; Task task = null; Stopwatch stopwatch = new Stopwatch(); stopwatch.Start(); Task[] allTasks = new Task[totalThreads]; for (int i = 0; i < totalThreads; i++) { task = Task.Factory.StartNew(() => { DoLongRunningWork(); },taskCreationOpt); allTasks[i] = task; } Task.WaitAll(allTasks); stopwatch.Stop(); Console.WriteLine(String.Format("Completed {0} tasks in {1} milliseconds",totalThreads,stopwatch.ElapsedMilliseconds)); Console.WriteLine(String.Format("Used {0} threads",threadIds.Count)); Console.ReadKey(); } private static List<int> threadIds = new List<int>(); private static object locker = new object(); private static void DoLongRunningWork() { lock (locker) { //Keep a record of the managed thread used. if (!threadIds.Contains(Thread.CurrentThread.ManagedThreadId)) threadIds.Add(Thread.CurrentThread.ManagedThreadId); } Guid g1 = Guid.NewGuid(); Trace.CorrelationManager.ActivityId = g1; Thread.Sleep(3000); Guid g2 = Trace.CorrelationManager.ActivityId; Debug.Assert(g1.Equals(g2)); } }
输出(当然这取决于机器)是:
Completed 100 tasks in 23097 milliseconds Used 23 threads
将taskCreationOpt更改为TaskCreationOptions.LongRunning提供了不同的结果:
Completed 100 tasks in 3458 milliseconds Used 100 threads