C#锁定和队列上的多线程

时间:2012-02-23 01:18:24

标签: c# multithreading concurrency locking

我正在编写一个多线程应用程序,我担心2个线程访问队列

线程1将项目放入队列进行处理 线程2从队列中删除要处理的项目

线程1每分钟运行一次,因为它正在拉动数据的性质。 线程2始终在运行,它从队列中删除一个项目并休眠100毫秒。我必须这样做,以确保我不会淹没它在项目出列时所调用的服务。

我假设在添加或删除项目时,两个线程都应该锁定队列。有进一步的考虑吗?例如,假设线程1有一个锁,而线程2试图访问它。线程2是否只知道在锁定被移除后等待并恢复?

使用ConcurrentQueue和TryDequeue会更好吗?如果它失败了,那么只需要100毫秒的睡眠时间吗?

提前致谢

4 个答案:

答案 0 :(得分:8)

如果你像我在NuGet的VS控制台调度程序中那样使用BlockingCollection<T>来实现我的PostKey / WaitKey,那就更容易了。消费线程调用Take(...),它将阻塞,直到另一个线程调用Add(...)。没有必要进行民意调查。此外,您可能希望将取消令牌传递给Take方法,以便另一个线程可以停止使用者线程,如果它当前正在等待永远不会来的Add。以下是相关方法:

private readonly BlockingCollection<VsKeyInfo> _keyBuffer = 
      new BlockingCollection<VsKeyInfo>();
private CancellationTokenSource _cancelWaitKeySource;

// place a key into buffer
public void PostKey(VsKeyInfo key)
{
    if (key == null)
    {
        throw new ArgumentNullException("key");
    }
    _keyBuffer.Add(key);
}

// signal thread waiting on a key to exit Take
public void CancelWaitKey()
{
    if (_isExecutingReadKey && !_cancelWaitKeySource.IsCancellationRequested)
    {
        _cancelWaitKeySource.Cancel();
    }
}

// wait for a key to be placed on buffer
public VsKeyInfo WaitKey()
{
    try
    {
        // raise the StartWaitingKey event on main thread
        RaiseEventSafe(StartWaitingKey);

        // set/reset the cancellation token
        _cancelWaitKeySource = new CancellationTokenSource();
        _isExecutingReadKey = true;

        // blocking call
        VsKeyInfo key = _keyBuffer.Take(_cancelWaitKeySource.Token);

        return key;
    }
    catch (OperationCanceledException)
    {
        return null;
    }
    finally
    {
        _isExecutingReadKey = false;
    }
}

有关详细信息,请参阅http://nuget.codeplex.com/SourceControl/changeset/view/45e353aca7f4#src%2fVsConsole%2fConsole%2fConsole%2fConsoleDispatcher.cs

答案 1 :(得分:2)

只要您锁定相同的“sync”对象,线程2将等待线程1,反之亦然。我认为ConcurrentQueue是一个好主意,因为它已被指定为线程安全。

答案 2 :(得分:1)

ConcurrentQueue是线程安全的,因此您不需要锁定,只需调用TryDequeue即可,没有其他特殊编码就可以了。不要迭代它,根据文档,它会在迭代时快照(除非那是你想要的):http://msdn.microsoft.com/en-us/library/dd287144.aspx

  

枚举表示队列内容的即时快照。在调用 GetEnumerator 后,它不会反映对集合的任何更新。枚举器可以安全地与队列的读取和写入同时使用。

老实说,我认为只要让线程1在需要完成任务并使用某种限制同步(如AutoResetEvent)Task),你会更好。 >

答案 3 :(得分:1)

如果你要睡100码,你可以任意方式实现队列,只要它是线程安全的。一个带锁/互斥锁的简单队列,一个ConcurrentQueue,它们都很好。

'例如,说线程1有一个锁,而线程2试图访问它。线程2只是知道等待并在锁定被移除后恢复“ - 线程2等待锁定被释放,是的。