我有一个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()的阻塞等待。有什么建议吗?
答案 0 :(得分:4)
这里有一个非常简单的方法:
public string WaitForIdleAndGetErrors()
{
while (queue.IsCompleted == false )
{
System.Threading.Thread.Current.Sleep(100);
}
return ErrorsOccurred;
}
或使用ManualResetEventSlim:
声明新实例var:
ManualResetEventSlim _mre = new ManualResetEventSlim(false);
public string WaitForIdleAndGetErrors()
{
if (queue.IsCompleted == false )
{
_mre.Wait();
}
return ErrorsOccurred;
}
然后,当您的队列完成时,请发出信号。
_mre.Set(); // this will release any thread waiting.
最后,当您添加项目进行处理时,您需要Reset()
_mre,这将导致任何Wait()
阻止,直到_mre发出信号(通过Set()
)
如果你用UI线程调用它,那么所有UI交互都会被冻结,你最好使用Timer进行轮询或类似的东西,否则你的UI体验会很糟糕。
但是,您可以使用BackgroundWorker
然后Invoke
在完成后处理UI线程的方法/事件来解决这一切。
答案 1 :(得分:4)
当队列为空时,线程将退出。所以你的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;
}
}
}
答案 2 :(得分:0)
要添加到以前的答案,如果要以非阻塞方式(Thread.Join阻止)或想要给调用者提供更多控制,可以将以下内容添加到ManualResetEventSlim示例中: / p>
// Calling Thread
...
imageWriter.WaitForIdleAndGetErrors.Wait(myDesiredWaitLimit);
...
然后,除了让您的UI(假设)线程空闲之外,您还可以控制是否要阻止呼叫,何时阻止任务以及如何 长。 可能的呼叫可能是:
if (choice1 == 4)
{
nbGamesEasy_Finished++;
successRate_Easy = nbGamesEasy_Finished / nbGames_Easy * 100;
System.out.println("How many time did you take to fill the grid ?");
System.out.println("The time must be in minutes");
String display5 = sudoku.nextLine();
resolutionTime_easy = (nbGamesEasy_Finished * resolutionTime_easy) + Integer.parseInt(display5) / (nbGamesEasy_Finished);
}
else if (choice1 == 5)
{
successRate_Easy = nbGamesEasy_Finished - 1 / nbGames_Easy * 100;
}
// and for default, use else
else
{
//this is the default statement
//you can do what you want here if not 4 or 5
}