如何检查.NET BlockingCollection状态并等待完成
本文关键字:状态 等待 BlockingCollection NET 何检查 检查 | 更新日期: 2023-09-27 17:57:35
我有一个C#工作线程,它使用BlockingCollection将一批相机位图保存到光盘中。它工作得很好,但我需要一个从主应用程序调用的方法,该方法会阻止执行,直到所有排队的位图都保存下来(例如,请参阅消息末尾)。
整个班级看起来像:
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
}
}
}
并且从主要应用程序中使用,如:
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();
因此,问题是如何实现对WaitForIdleAndGetErrors()的阻塞等待。有什么建议吗?
这里有一个非常简单的方法:
public string WaitForIdleAndGetErrors()
{
while (queue.IsCompleted == false )
{
System.Threading.Thread.Current.Sleep(100);
}
return ErrorsOccurred;
}
或者使用手动重置事件Slim:
声明新实例var:
ManualResetEventSlim _mre = new ManualResetEventSlim(false);
public string WaitForIdleAndGetErrors()
{
if (queue.IsCompleted == false )
{
_mre.Wait();
}
return ErrorsOccurred;
}
然后,当您的队列完成时,向mre发出信号。
_mre.Set(); // this will release any thread waiting.
最后,当添加一个项目进行处理时,您需要Reset()
_mre,这将导致任何Wait()
阻塞,直到_mre被发送信号(通过Set()
)
需要考虑的事项
如果你用UI线程调用它,那么所有的UI交互都将显示为冻结,你最好使用定时器来轮询或类似的东西,否则你会有糟糕的UI体验。
但是,您可以使用BackgroundWorker
和Invoke
(UI线程将在完成后处理的方法/事件)来启动整个过程。
当队列为空时,线程将退出。因此,您的WaitForIdleAndGetErrors
方法只需要等待线程结束。Thread.Join就是这么做的:
public string WaitForIdleAndGetErrors()
{
// Wait for thread to exit
writerthread.Join();
return ErrorsOccurred;
}
Thread.Join
进行非繁忙等待。在等待线程退出时,您不会消耗CPU,也不需要单独的事件。
顺便说一句,你可以利用BlockingCollection.GetConsumingEnumerable:来简化你的线程
void Writer()
{
foreach (SaveTask t in queue.GetConsumingEnumerable())
{
try
{
SaveBitmap(t.fname, t.bm);
}
catch (Exception e)
{
//comes here after called Stop
return;
}
}
}
要添加到前面的答案中,如果您想以非阻塞的方式(Thread.Join正在阻塞)或想给调用者更多的控制权,您可以将以下内容添加到ManualResetEventSlim示例中:
ManualResetEventSlim _mre = new ManualResetEventSlim(false);
public Task<string> WaitForIdleAndGetErrors()
{
return Task.Factory.StartNew(() =>
{
if (!_queue.IsCompleted)
{
_mre.Wait();
}
return ErrorsOccurred;
});
}
然后,除了UI(假设)线程空闲之外,您还可以控制是否要阻止调用、何时阻止任务以及如何阻止长的可能的呼叫可能是:
// Calling Thread
...
imageWriter.WaitForIdleAndGetErrors.Wait(myDesiredWaitLimit);
...