任务并行库中的任务如何影响ActivityID?

时间:2010-12-02 23:07:53

标签: c# .net multithreading task

在使用任务并行库之前,我经常使用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很容易在任务中重新初始化(通过另一项任务)?是否有新的机制来处理活动追踪?

2 个答案:

答案 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)