ExecutionContext.Capture()和ExecutionContext.Run(上下文,工作,状态)的成本

时间:2016-01-08 22:43:02

标签: c# .net multithreading performance

有人知道ExecutionContext.Capture()ExecutionContext.Run(context, work, state)是否代价高昂?

是否会降低性能,建议小心使用?

我问,因为我有一个ContextItem,我将Context工作和状态保存在以后执行。因为我希望能够对执行工作时可能抛出的异常做出反应,所以如果在工作中抛出异常,我会执行回退。而且我也有最后的工作,无论如何抛出异常都会在任何情况下执行。 由于我只能使用一次ExecutionContext,因此对于其中一个ContextItem,我必须ExecutionContext.Capture()三次...

或者这听起来像是一种完全错误的做法?

1 个答案:

答案 0 :(得分:1)

根据@Alois Kraus的建议,我使用以下代码进行了测试,将锁定与捕获和放大相比较。排队执行:

class Program
{
    private static readonly object _lock = new object();
    private static readonly int numberOfItems = 1000000;
    private static readonly int _numberOfIterations = 1000000;

    private static void Main(string[] args)
    {
        MeasureTimeWithLocking();
        MeasureTimeWithCapuringContext();
        Console.WriteLine();
        MeasureTimeWithLocking();
        MeasureTimeWithCapuringContext();
        Console.WriteLine();
        MeasureTimeWithLocking();
        MeasureTimeWithCapuringContext();
        Console.ReadKey();
    }

    private static void MeasureTimeWithLocking()
    {
        List<ContextItem> items = new List<ContextItem>();
        Stopwatch stopwatch = Stopwatch.StartNew();
        for (int i = 0; i < numberOfItems; i++)
        {
            ContextItem item = new ContextItem();
            item.Work1 = DoSomeWorkWithLock;
            item.Work2 = DoSomeWorkWithLock;
            item.Work3 = DoSomeWorkWithLock;
        }

        Parallel.ForEach(items, (item) =>
        {
            item.Work1(null);
            item.Work2(null);
            item.Work3(null);
        });
        stopwatch.Stop();
        Console.WriteLine("Time elapsed with locking:           " + stopwatch.Elapsed);
    }

    private static void MeasureTimeWithCapuringContext()
    {
        List<ContextItem> items = new List<ContextItem>();
        Stopwatch stopwatch = Stopwatch.StartNew();
        for (int i = 0; i < numberOfItems; i++)
        {
            ContextItem item = new ContextItem();
            item.Context1 = ExecutionContext.Capture();
            item.Context2 = ExecutionContext.Capture();
            item.Context3 = ExecutionContext.Capture();
            item.Work1 = DoSomeWork;
            item.Work2 = DoSomeWork;
            item.Work3 = DoSomeWork;
        }

        foreach (ContextItem item in items)
        {
            ExecutionContext.Run(item.Context1, item.Work1, null);
            ExecutionContext.Run(item.Context2, item.Work2, null);
            ExecutionContext.Run(item.Context3, item.Work3, null);
        }
        stopwatch.Stop();
        Console.WriteLine("Time elapsed with capturing context: " + stopwatch.Elapsed);
    }

    private static void DoSomeWork(object ignored)
    {
        Work();
    }


    private static void DoSomeWorkWithLock(object ignored)
    {
        lock (_lock)
        {
            Work();
        }
    }

    private static void Work()
    {
        int count = 0;
        for (int i = 0; i < _numberOfIterations; i++)
        {
            count ++;
        }
    }

    private class ContextItem
    {
        public ExecutionContext Context1 { get; set; }
        public ExecutionContext Context2 { get; set; }
        public ExecutionContext Context3 { get; set; }

        public ContextCallback Work1 { get; set; }
        public ContextCallback Work2 { get; set; }
        public ContextCallback Work3 { get; set; }
    }
}

结果是:

enter image description here

所以,如果我这样做,捕捉&amp;执行排队的费用平均比锁定贵5倍左右。

还要回答我的问题部分:

  

或者这听起来像是一种完全错误的做法?

我在this article读到了

  

如果你必须知道他们在那里,要么你正在做一些超级先进的事情,要么就是出了问题。

如果您想了解ExecutionContext,那么建议将SO作为最佳来源。 经过它并与同事一起运行一些测试之后,我意识到我使用的是ExecutionContext,它没有意义,而且它的性能比锁定更低,所以它可能也比其他线程功能/结构的性能更差。 / p>