Jordan
Jordan

Reputation: 2758

C# Locks and Multi Threading on a queue

I am writing a multi threaded application and I have concerns with 2 threads access a queue

Thread 1 puts items in a queue for processing Thread 2 removes items from the queue to process

Thread 1 runs once a minute due to the nature of the data it is pulling. Thread 2 is always running, it removes an item from the queue and sleeps for 100ms. I have to do this to ensure that I don't overwhelm a service it calls when it dequeues an item.

I assume both threads should place a lock on the queue when adding or removing items from it. Are there any further considerations? For instance say Thread 1 has a lock and Thread 2 tries to access it. Does Thread 2 simply know to wait and resume once the lock is removed?

Would it be preferable to use a ConcurrentQueue and just TryDequeue and if it fails just go about its 100 ms sleep?

Thanks in advance

Upvotes: 2

Views: 2306

Answers (4)

x0n
x0n

Reputation: 52430

It's even easier if you use a BlockingCollection<T> like I do in NuGet's VS console dispatcher for my PostKey/WaitKey implementation. The consuming thread calls Take(...) which will block until another thread calls Add(...). There is no need to poll. Additionally, you may wish to pass a cancellation token to the Take method so another thread can stop the consumer thread if it's currently waiting for an Add that will never come. Here are the relevant methods:

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;
    }
}

See http://nuget.codeplex.com/SourceControl/changeset/view/45e353aca7f4#src%2fVsConsole%2fConsole%2fConsole%2fConsoleDispatcher.cs for more details.

Upvotes: 8

Martin James
Martin James

Reputation: 24857

If you're going to sleep for 100ms anyway, you can pretty much implement the queue any way you want as long as it's thread-safe. A simple queue with a lock/mutex, a ConcurrentQueue, they're all fine.

'For instance say Thread 1 has a lock and Thread 2 tries to access it. Does Thread 2 simply know to wait and resume once the lock is removed' - Thread 2 waits until the lock is released, yes.

Upvotes: 1

Paul Tyng
Paul Tyng

Reputation: 7584

ConcurrentQueue is thread safe so you wouldn't need to lock, just call TryDequeue and you will be fine with no other special coding. Do not iterate it though, per the documentation, it will snapshot on iteration (unless that was what you wanted): http://msdn.microsoft.com/en-us/library/dd287144.aspx

The enumeration represents a moment-in-time snapshot of the contents of the queue. It does not reflect any updates to the collection after GetEnumerator was called. The enumerator is safe to use concurrently with reads from and writes to the queue.

Honestly I think you would be better off just having Thread 1 spawn a thread (or Task) whenever it needs something done and using some sort of throttling synchronization (like an AutoResetEvent)

Upvotes: 1

swannee
swannee

Reputation: 3466

As long as you are locking on the same "sync" object, thread 2 will wait for thread 1 and vice-versa. I think the ConcurrentQueue is a good idea because it's already specified as thread safe.

Upvotes: 2

Related Questions