简体   繁体   中英

thread safe processing of a queue

I am putting tasks from the UI-thread into a queue, so that they can be processed in another thread. if there is nothing to do, the thread should wait with an AutoResetEvent - obviously all this should be threadsafe.

i am putting tasks in the queue from the UI-thread like this:

lock (_syncObject)
{
    _queue.Enqueue(new FakeTask());
}

    _autoResetEvent.Set();

here is how my processing thread-loop looks so far:

while (true)
{
    FakeTask task = null;
    lock (_syncObject)
    {
        if (_queue.Count > 0)
        {
            task = _queue.Dequeue();
        }
    }

    if (task != null)
    {
        task.Run();
        Thread.Sleep(1000);  //just here for testing purposes
    }

    if (_queue.Count == 0)
    {
        _autoResetEvent.WaitOne();
    }
}

i am afraid that the last part where i check if something else is in the queue is not thread safe and would like to know how i can make it so. thanks!

In simple case , try using BlockingCollection which has been specially designed for implementing Producer / Consumer pattern :

private async Task Process() {
  using (BlockingCollection<FakeTask> _queue = new BlockingCollection<FakeTask>()) {
    Task producer = Task.Run(() => {
      while (!completed) {
        //TODO: put relevant code here 
        _queue.Add(new FakeTask());
      }

      _queue.CompleteAdding();
    });

    Task consumer = Task.Run(() => {
      foreach (FakeTask task in _queue.GetConsumingEnumerable()) {
        //TODO: process task - put relevant code here
      }
    });

    await Task.WhenAll(producer, consumer).ConfigureAwait(false); 
  }
}

Edit: if producer is UI thread itself:

private async Task Process() {
  using (BlockingCollection<FakeTask> _queue = new BlockingCollection<FakeTask>()) {
    Task consumer = Task.Run(() => {
      foreach (FakeTask task in _queue.GetConsumingEnumerable()) {
        //TODO: process task - put relevant code here
      }
    });

    // If we produce in UI we don't want any separate Task 
    while (!completed) {
      //TODO: put relevant code here 
      _queue.Add(new FakeTask());
    }

    _queue.CompleteAdding();

    await consumer.ConfigureAwait(false); 
  }
}

In case of entangled mesh (eg producers #1, #2 genetate tasks for consumers #1, #2, #3 which in turn create tasks for...) try DataFlow

It's not useful to create a thread just so that it can spend basically all of its time sitting around doing nothing waiting for work to do.

All you need to do is use an asynchronous locking mechanism around the UI task's scheduling of background work to be done. SemaphoreSlim provides such an asynchronous synchronization mechanism.

await sempahoreSlim.WaitAsync();
try
{
    await Task.Run(() => DoWork());
}
finally
{
    sempahoreSlim.Release();
}

Not only is it a lot less code, and has much simpler code that more accurately reflects what the business logic of the application is, but you're consuming quite a lot less system resources.

And of course if different background operations can safely run in parallel, then just use the thread pool, rather than your own message loop, and the code becomes even simpler.

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM