C# - 如何并行锁定多个对象的代码

时间:2013-01-03 16:43:02

标签: c# locking

我有这样的代码:

public void TablesUpdated(object sender, TablesUpdatedArgs args)
{
    lock (quotesLock)
    {
        while (!args.quotesQueue.IsNullOrEmpty())
            quotes.Enqueue(args.quotesQueue.Dequeue());
    }
    lock (securitiesLock)
    {
        while (!args.securitiesUpdates.IsNullOrEmpty())
            securities.Enqueue(args.securitiesUpdates.Dequeue());
    }
    lock (orderUpdatesLock)
    {
        while (!args.orderUpdates.IsNullOrEmpty())
            orderUpdates.Enqueue(args.orderUpdates.Dequeue());
    }
}

此代码存在的问题是我正在等待lock,而我可能会处理代码的其他部分。虽然我在等待代码的其他部分可能会被锁定!

假设quotesLock在0到1秒之间忙,securitiesLock在1到2秒之间忙,orderUpdatesLock在2到3秒之间忙。由于订单,我的代码将被完全阻止3个seconods。

但如果qoutesLock将是最后一个:

public void TablesUpdated(object sender, TablesUpdatedArgs args)
{
    lock (securitiesLock)
    {
        while (!args.securitiesUpdates.IsNullOrEmpty())
            securities.Enqueue(args.securitiesUpdates.Dequeue());
    }
    lock (orderUpdatesLock)
    {
        while (!args.orderUpdates.IsNullOrEmpty())
            orderUpdates.Enqueue(args.orderUpdates.Dequeue());
    }
    lock (quotesLock)
    {
        while (!args.quotesQueue.IsNullOrEmpty())
            quotes.Enqueue(args.quotesQueue.Dequeue());
    }
}

代码将在1秒内执行。

问题是如何重写代码:

  • 如果无法获取某些锁,则其他锁正在处理
  • 没有创建额外的线程(因为这太贵了)。

可能我应该用很多while方法编写非常复杂的TryEnter循环。或者更好的是什么?

实际锁定中的

upd 持续很短的时间(约5-15微秒)。所以去另一个线程可能不是一个好主意,我认为一切都应该在同一个线程中完成。

1 个答案:

答案 0 :(得分:2)

由于这些任务完全不依赖于彼此,因此最好将它们平行化。

这是一种方法:

Parallel.Invoke(new ParallelOptions(), () =>
    {
        lock (securitiesLock)
        {
            while (!args.securitiesUpdates.IsNullOrEmpty())
                securities.Enqueue(args.securitiesUpdates.Dequeue());
        }
    },
    () =>
    {
        lock (orderUpdatesLock)
        {
            while (!args.orderUpdates.IsNullOrEmpty())
                orderUpdates.Enqueue(args.orderUpdates.Dequeue());
        }
    },
    () =>
    {
        lock (quotesLock)
        {
            while (!args.quotesQueue.IsNullOrEmpty())
                quotes.Enqueue(args.quotesQueue.Dequeue());
        }
    });

由于你所说的锁被持有相当长的一段时间,这很可能是一场净胜利。因为它将依赖于线程池线程(因此很可能甚至不需要创建3个硬线程,尽管如果它们已经存在它们就可以使用它们),您可以消除大部分线程开销。如果不出意外,它可以选择针对当前选项和您创建的任何其他解决方案进行基准测试。