在使用任务并行库之前,我经常使用CorrelationManager.ActivityId来跟踪多线程的跟踪/错误报告。
ActivityId存储在线程本地存储中,因此每个线程都获得自己的副本。这个想法是当你启动一个线程(活动)时,你分配一个新的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很容易在任务中重新初始化(通过另一项任务)?是否有新的机制来处理活动追踪?
答案 0 :(得分:6)
我进行了一些实验,结果发现我的问题中的假设是错误的 - 使用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
答案 1 :(得分:3)