patterncsharpMinor
Process dependent results of Producer/Consumer queue with explicit locking
Viewed 0 times
processwithexplicitconsumerlockingresultsproducerqueuedependent
Problem
I have seen many examples which the results of a Producer/Consumer queue implemented using
I wonder whether explicit locking can be avoided if the consumers can NOT independently process each result calculated by producers. I created a demo example of this where each result has to be processed taking into account previous processed results in order to either insert a row to the data grid or update an existing data grid; it seems to work ok on some small data.
My questions are:
-
Can explicit locking be avoided?
-
How to encapsulate consumer and producer within a single object e.g.
-
Currently I use a separate task to process each result especially given it ultimately has to post back to the UI thread; is this a waste?
Below is the code behind a simple WinForm which only has a DataGridView and a button.
```
using System;
using System.Collections.Concurrent;
using System.Threading;
using System.Threading.Tasks;
using System.Windows.Forms;
namespace PCQueueDemo
{
public partial class Form1 : Form
{
// to consume results that are depedent
private ConcurrentDictionary _dict = new ConcurrentDictionary();
private readonly object _sync = new object();
private BindingSource _bindingSource = new BindingSource();
private TaskScheduler _ui;
private ProducerConsumerQueue _producers;
private BlockingCollection _workQueue;
public Form1()
{
InitializeComponent();
_workQueue = new BlockingCollection();
// create 3 producers which will produce work item to the work queue.
_producers = new ProducerConsumerQueue(3, _workQueue);
BlockingCollection are independent, e.g. each result can be consumed independently in parallel. For example, here the produces calculate a square root and consumers grab each result to update the UI.I wonder whether explicit locking can be avoided if the consumers can NOT independently process each result calculated by producers. I created a demo example of this where each result has to be processed taking into account previous processed results in order to either insert a row to the data grid or update an existing data grid; it seems to work ok on some small data.
My questions are:
-
Can explicit locking be avoided?
-
How to encapsulate consumer and producer within a single object e.g.
ProducerConsumerQueue which currently is really a producer queue.-
Currently I use a separate task to process each result especially given it ultimately has to post back to the UI thread; is this a waste?
Below is the code behind a simple WinForm which only has a DataGridView and a button.
```
using System;
using System.Collections.Concurrent;
using System.Threading;
using System.Threading.Tasks;
using System.Windows.Forms;
namespace PCQueueDemo
{
public partial class Form1 : Form
{
// to consume results that are depedent
private ConcurrentDictionary _dict = new ConcurrentDictionary();
private readonly object _sync = new object();
private BindingSource _bindingSource = new BindingSource();
private TaskScheduler _ui;
private ProducerConsumerQueue _producers;
private BlockingCollection _workQueue;
public Form1()
{
InitializeComponent();
_workQueue = new BlockingCollection();
// create 3 producers which will produce work item to the work queue.
_producers = new ProducerConsumerQueue(3, _workQueue);
Solution
Can explicit locking be avoided?
Sure, you can use immutable data structures with interlocked replacements. It's important to note that locking isn't bad, though - even
I'm not a huge fan of lockfree algorithms because they're incredibly complex, particularly with more relaxed memory models.
How to encapsulate consumer and producer within a single object e.g. ProducerConsumerQueue which currently is really a producer queue.
I would say it's a queue combined with producer logic. I'm not sure if you'd want to combine producer and consumer logic within the same class.
Currently I use a separate task to process each result; is this a waste?
For general producer/consumer systems, I would say no. In this particular case, I'd say yes, because every consumer task is run on the UI thread. This means they run one at a time. As a consequence of this, they do not need locks.
Sure, you can use immutable data structures with interlocked replacements. It's important to note that locking isn't bad, though - even
BlockingCollection uses locks internally.I'm not a huge fan of lockfree algorithms because they're incredibly complex, particularly with more relaxed memory models.
How to encapsulate consumer and producer within a single object e.g. ProducerConsumerQueue which currently is really a producer queue.
I would say it's a queue combined with producer logic. I'm not sure if you'd want to combine producer and consumer logic within the same class.
Currently I use a separate task to process each result; is this a waste?
For general producer/consumer systems, I would say no. In this particular case, I'd say yes, because every consumer task is run on the UI thread. This means they run one at a time. As a consequence of this, they do not need locks.
Context
StackExchange Code Review Q#161122, answer score: 4
Revisions (0)
No revisions yet.