TeroK TeroK - 1 month ago 14
C# Question

How to check .NET BlockingCollection status and wait for completion?

I have a C# worker thread that saves batch of camera bitmaps to disc in by using BlockingCollection. It works nicely but I need a method to be called from main app that blocks execution until all queued bitmaps are saved (see end of message for example).

The whole class looks like:

namespace GrabGUI
{
struct SaveTask
{
public string fname;
public Bitmap bm;
}

class ImageWriter
{
private BlockingCollection<SaveTask> queue = new BlockingCollection<SaveTask>();

//resets when read
public string ErrorsOccurred;
private Thread writerthread;

public ImageWriter()
{
writerthread = new Thread(new ThreadStart(Writer));
writerthread.Start();
}


public void Stop()
{
queue.CompleteAdding();
}

public string WaitForIdleAndGetErrors()
{
//HOW TO WAIT FOR QUEUE TO GET PROCESSED?

return ErrorsOccurred;
}

public void AddImageToQueue(string filename, Bitmap bmap)
{
SaveTask t;
t.bm=bmap;
t.fname=filename;
queue.Add(t);
}

void Writer()
{
while (queue.IsCompleted==false)
{
try
{
SaveTask t = queue.Take();// blocks when the queue is empty
SaveBitmap(t.fname, t.bm);
}
catch (Exception e)
{
//comes here after called Stop
return;
}
}
}


private void SaveBitmap(string filename,Bitmap m_bitmap)
{
//saving code
}

}
}


And is used from main app like:

ImageWriter w=new ImageWriter();

w.AddImageToQueue(fname,bitmap);//repeat many times
...

//wait until whole queue is completed and get possible errors that occurred
string errors=w.WaitForIdleAndGetErrors();


So the question is how to implement the blocking wait to WaitForIdleAndGetErrors(). Any suggestions?

Answer

One very simple way here:

public string WaitForIdleAndGetErrors()
{
    while (queue.IsCompleted == false )
    {
       System.Threading.Thread.Current.Sleep(100);
    }

   return ErrorsOccurred;
}

Or use a ManualResetEventSlim:

Declare new instance var:

ManualResetEventSlim _mre = new ManualResetEventSlim(false);

public string WaitForIdleAndGetErrors()
{
    if (queue.IsCompleted == false )
    {
       _mre.Wait();
    }

   return ErrorsOccurred;
}

Then when your queue is complete signal the mre.

_mre.Set();   // this will release any thread waiting.

Finally, you need to Reset() the _mre when an item is added for processing, this will cause any Wait() to block until the _mre is signaled (via Set())

Things to consider

If you call this with the UI Thread then all UI interaction will appear to be frozen, you'd be better off using a Timer to poll or something similar otherwise you will have a bad UI experience.

However you could fire this whole thing off using a BackgroundWorker and then Invoke a method/event that the UI thread will process upon completion.