是Queue.Synchronized比使用Lock()更快?

Dan*_*han 7 c# queue performance multithreading .net-3.5

我有一个Queue,其中Enqueue操作将由一个线程执行,Dequeue将由另一个执行.不用说,我必须为它实现一些线程安全性.

我首先尝试在每个Enqueue/Dequeue之前使用队列上的锁,因为它可以更好地控制锁定机制.它运作良好,但我的好奇心使我进行了更多测试.

然后我尝试使用Queue.Synchronized包装器保持其他所有内容相同.现在,我不确定它是否真实,但这种方法的性能似乎有点快.

你觉得,两者之间的性能确实存在一些差异,或者我只是想象这里的东西......?:)

Tim*_*oyd 17

当请求Queue.Synchonized你得到一个SynchronizedQueue回报时,它使用一个lock非常小的呼叫到内部队列EnqueueDequeue内部队列.因此,性能应该是一样的使用Queue和管理自己锁定为EnqueueDequeue你自己lock.

你确实在想象 - 他们应该是一样的.

更新

事实上,当SynchronizedQueue你使用a时,你需要添加一个间接层,因为你必须通过包装器方法来到它正在管理的内部队列.如果有任何事情,这会使事情变得非常缓慢,因为你需要为每次调用管理堆栈上的额外帧.上帝知道内衬是否取消了这一点.无论如何 - 它是最小的.

更新2

我现在已经对此进行了基准测试,正如我之前的更新所预测的那样:

"Queue.Synchronized"比"Queue + lock"慢

我进行了单线程测试,因为他们都使用相同的锁定技术(即lock),因此在"直线"中测试纯粹的开销似乎是合理的.

我的基准测试为Release版本生成了以下结果:

Iterations      :10,000,000

Queue+Lock      :539.14ms
Queue+Lock      :540.55ms
Queue+Lock      :539.46ms
Queue+Lock      :540.46ms
Queue+Lock      :539.75ms
SynchonizedQueue:578.67ms
SynchonizedQueue:585.04ms
SynchonizedQueue:580.22ms
SynchonizedQueue:578.35ms
SynchonizedQueue:578.57ms
Run Code Online (Sandbox Code Playgroud)

使用以下代码:

private readonly object _syncObj = new object();

[Test]
public object measure_queue_locking_performance()
{
    const int TestIterations = 5;
    const int Iterations = (10 * 1000 * 1000);

    Action<string, Action> time = (name, test) =>
    {
        for (int i = 0; i < TestIterations; i++)
        {
            TimeSpan elapsed = TimeTest(test, Iterations);
            Console.WriteLine("{0}:{1:F2}ms", name, elapsed.TotalMilliseconds);
        }
    };

    object itemOut, itemIn = new object();
    Queue queue = new Queue();
    Queue syncQueue = Queue.Synchronized(queue);

    Action test1 = () =>
    {
        lock (_syncObj) queue.Enqueue(itemIn);
        lock (_syncObj) itemOut = queue.Dequeue();
    };

    Action test2 = () =>
    {
        syncQueue.Enqueue(itemIn);
        itemOut = syncQueue.Dequeue();
    };

    Console.WriteLine("Iterations:{0:0,0}\r\n", Iterations);
    time("Queue+Lock", test1);
    time("SynchonizedQueue", test2);

    return itemOut;
}

[SuppressMessage("Microsoft.Reliability", "CA2001:AvoidCallingProblematicMethods", MessageId = "System.GC.Collect")]
private static TimeSpan TimeTest(Action action, int iterations)
{
    Action gc = () =>
    {
        GC.Collect();
        GC.WaitForFullGCComplete();
    };

    Action empty = () => { };

    Stopwatch stopwatch1 = Stopwatch.StartNew();

    for (int j = 0; j < iterations; j++)
    {
        empty();
    }

    TimeSpan loopElapsed = stopwatch1.Elapsed;

    gc();
    action(); //JIT
    action(); //Optimize

    Stopwatch stopwatch2 = Stopwatch.StartNew();

    for (int j = 0; j < iterations; j++) action();

    gc();

    TimeSpan testElapsed = stopwatch2.Elapsed;

    return (testElapsed - loopElapsed);
}
Run Code Online (Sandbox Code Playgroud)

  • 上面测试中的值与使用 ConcurrentQueue 相比如何? (2认同)

jas*_*son 6

我们无法为您解答此问题.只有您可以通过获取分析器并测试应用程序中的实际数据的两种方案(与之Queue.Synchronized对比Lock)来自行回答.它甚至可能不是您的应用程序的瓶颈.

那就是说,你应该只是在使用ConcurrentQueue.