.net 如何正确等待BackgroundWorker完成?
请遵守以下代码:.net 如何正确等待BackgroundWorker完成?,.net,multithreading,backgroundworker,.net,Multithreading,Backgroundworker,请遵守以下代码: var handler = GetTheRightHandler(); var bw = new BackgroundWorker(); bw.RunWorkerCompleted += OnAsyncOperationCompleted; bw.DoWork += OnDoWorkLoadChildren; bw.RunWorkerAsync(handler); 现在假设我想等到bw完成工作。这样做的正确方式是什么 我的解决办法是: bool finished = fals
var handler = GetTheRightHandler();
var bw = new BackgroundWorker();
bw.RunWorkerCompleted += OnAsyncOperationCompleted;
bw.DoWork += OnDoWorkLoadChildren;
bw.RunWorkerAsync(handler);
现在假设我想等到bw
完成工作。这样做的正确方式是什么
我的解决办法是:
bool finished = false;
var handler = GetTheRightHandler();
var bw = new BackgroundWorker();
bw.RunWorkerCompleted += (sender, args) =>
{
OnAsyncOperationCompleted(sender, args);
finished = true;
});
bw.DoWork += OnDoWorkLoadChildren;
bw.RunWorkerAsync(handler);
int timeout = N;
while (!finished && timeout > 0)
{
Thread.Sleep(1000);
--timeout;
}
if (!finished)
{
throw new TimedoutException("bla bla bla");
}
但是我不喜欢它
我已经考虑过用同步事件替换finished
标志,在RunWorkerCompleted
处理程序中设置它,稍后再对其执行块,而不是执行while sleep循环
唉,这是错误的,因为代码可能在WPF或WindowsForm同步上下文中运行,在这种情况下,我会阻止运行RunWorkerCompleted
处理程序的线程,这显然不是很明智的做法
我想知道一个更好的解决办法
谢谢
编辑:
附言
- 示例代码是如此精心设计以澄清我的问题。我完全了解完成回调,但我想知道如何等待完成。这是我的问题
- 我知道
,Thread.Join
,Delegate.BeginInvoke
,等等。。。这个问题是关于ThreadPool.QueueUserWorkItem
后台工作人员的
BackgroundWorker
结合,我可以向其传递一个完成处理程序。所有的代码都是我的,所以如果我愿意,我可以更改实现。
但是,我不打算替换BackgroundWorker
,因为它会自动使用正确的同步上下文,因此当在UI线程上调用代码时,会在同一UI线程上调用完成回调,这非常好
无论如何,单元测试方法有可能在BW完成其工作之前结束,这是不好的。因此,我希望等到BW完成,并想知道它的最佳方式
有更多的片段,但总体情况与我刚才描述的差不多。BackgroundWorker有一个完成事件。不要等待,而是从完成处理程序调用剩余的代码路径。尝试使用AutoResetEvent类,如下所示:
var doneEvent = new AutoResetEvent(false);
var bw = new BackgroundWorker();
bw.DoWork += (sender, e) =>
{
try
{
if (!e.Cancel)
{
// Do work
}
}
finally
{
doneEvent.Set();
}
};
bw.RunWorkerAsync();
doneEvent.WaitOne();
注意:无论发生什么情况,都应确保调用doneEvent.Set()
。另外,您可能希望为doneEvent.WaitOne()
提供一个指定超时时间的参数
注意:这段代码几乎是一个答案的副本。不太清楚你所说的等待是什么意思。你的意思是你想做某事(通过BW)在那之后你想做其他的事情吗? 像您一样使用bw.RunWorkerCompleted(使用一个单独的函数以提高可读性),并在该回调函数中执行下一步操作。 启动计时器,检查工作是否耗时过长
var handler = GetTheRightHandler();
var bw = new BackgroundWorker();
bw.RunWorkerCompleted += (sender, args) =>
{
OnAsyncOperationCompleted(sender, args);
});
bw.DoWork += OnDoWorkLoadChildren;
bw.RunWorkerAsync(handler);
Timer Clock=new Timer();
Clock.Interval=1000;
Clock.Start();
Clock.Tick+=new EventHandler(Timer_Tick);
public void Timer_Tick(object sender,EventArgs eArgs)
{
if (bw.WorkerSupportsCancellation == true)
{
bw.CancelAsync();
}
throw new TimedoutException("bla bla bla");
}
在OnDoWorkLoadChildren中:
if ((worker.CancellationPending == true))
{
e.Cancel = true;
//return or something
}
要等待后台工作线程(单个或多个),请执行以下操作:
private IList<BackgroundWorker> m_WorkersWithData = new List<BackgroundWorker>();
private void CheckAllThreadsHaveFinishedWorking()
{
bool hasAllThreadsFinished = false;
while (!hasAllThreadsFinished)
{
hasAllThreadsFinished = (from worker in m_WorkersWithData
where worker.IsBusy
select worker).ToList().Count == 0;
Application.DoEvents(); //This call is very important if you want to have a progress bar and want to update it
//from the Progress event of the background worker.
Thread.Sleep(1000); //This call waits if the loop continues making sure that the CPU time gets freed before
//re-checking.
}
m_WorkersWithData.Clear(); //After the loop exits clear the list of all background workers to release memory.
//On the contrary you can also dispose your background workers.
}
这个问题由来已久,但我认为作者没有得到他想要的答案 这有点脏,它在vb.NET中,但对我有用
Private Sub MultiTaskingForThePoor()
Try
'Start background worker
bgwAsyncTasks.RunWorkerAsync()
'Do some other stuff here
For i as integer = 0 to 100
lblOutput.Text = cstr(i)
Next
'Wait for Background worker
While bgwAsyncTasks.isBusy()
Windows.Forms.Application.DoEvents()
End While
'Voila, we are back in sync
lblOutput.Text = "Success!"
Catch ex As Exception
MsgBox("Oops!" & vbcrlf & ex.Message)
End Try
End Sub
VB.NET
您可以使用它来链接多个事件。(遵循sudo代码)
在OpenCV中存在函数WaitKey。Ir允许通过以下方式解决此问题:
while (this->backgroundWorker1->IsBusy) {
waitKey(10);
std::cout << "Wait for background process: " << std::endl;
}
this->backgroundWorker1->RunWorkerAsync();
while(此->backgroundWorker1->IsBusy){
等候室(10);
std::cout RunWorkerAsync();
我也在寻找合适的解决方案。我用独占锁解决了等待问题。代码中的关键路径是写入公共容器(这里是控制台)并增加或减少工作线程。在写入此变量时,线程不应干涉,否则计数将不再得到保证
public class Program
{
public static int worker = 0;
public static object lockObject = 0;
static void Main(string[] args)
{
BackgroundworkerTest backgroundworkerTest = new BackgroundworkerTest();
backgroundworkerTest.WalkDir("C:\\");
while (backgroundworkerTest.Worker > 0)
{
// Exclusive write on console
lock (backgroundworkerTest.ExclusiveLock)
{
Console.CursorTop = 4; Console.CursorLeft = 1;
var consoleOut = string.Format("Worker busy count={0}", backgroundworkerTest.Worker);
Console.Write("{0}{1}", consoleOut, new string(' ', Console.WindowWidth-consoleOut.Length));
}
}
}
}
public class BackgroundworkerTest
{
private int worker = 0;
public object ExclusiveLock = 0;
public int Worker
{
get { return this.worker; }
}
public void WalkDir(string dir)
{
// Exclusive write on console
lock (this.ExclusiveLock)
{
Console.CursorTop = 1; Console.CursorLeft = 1;
var consoleOut = string.Format("Directory={0}", dir);
Console.Write("{0}{1}", consoleOut, new string(' ', Console.WindowWidth*3 - consoleOut.Length));
}
var currentDir = new System.IO.DirectoryInfo(dir);
DirectoryInfo[] directoryList = null;
try
{
directoryList = currentDir.GetDirectories();
}
catch (UnauthorizedAccessException unauthorizedAccessException)
{
// No access to this directory, so let's leave
return;
}
foreach (var directoryInfo in directoryList)
{
var bw = new BackgroundWorker();
bw.RunWorkerCompleted += (sender, args) =>
{
// Make sure that this worker variable is not messed up
lock (this.ExclusiveLock)
{
worker--;
}
};
DirectoryInfo info = directoryInfo;
bw.DoWork += (sender, args) => this.WalkDir(info.FullName);
lock (this.ExclusiveLock)
{
// Make sure that this worker variable is not messed up
worker++;
}
bw.RunWorkerAsync();
}
}
}
使用
Application.DoEvents()
在循环中检查backgrWorker.IsBusy
,这不是一个好方法
我同意@JohannesH的观点,你应该明确地使用AutoResetEvent作为一个优雅的解决方案。但如果不在UI线程中使用它,它将导致主线程阻塞;它应该来自另一个后台工作线程
AutoResetEvent aevent = new AutoResetEvent(false);
private void button1_Click(object sender, EventArgs e)
{
bws = new BackgroundWorker();
bws.DoWork += new DoWorkEventHandler(bw_work);
bws.RunWorkerCompleted += new RunWorkerCompletedEventHandler(bw_complete);
bws.RunWorkerAsync();
bwWaiting.DoWork += new DoWorkEventHandler(waiting_work);
bwWaiting.RunWorkerCompleted += new RunWorkerCompletedEventHandler(waiting_complete);
bwWaiting.RunWorkerAsync();
}
void bw_work(object sender, DoWorkEventArgs e)
{
Thread.Sleep(2000);
}
void bw_complete(object sender, RunWorkerCompletedEventArgs e)
{
Debug.WriteLine("complete " + bwThread.ToString());
aevent.Set();
}
void waiting_work(object sender, DoWorkEventArgs e)
{
aevent.WaitOne();
}
void waiting_complete(object sender, RunWorkerCompletedEventArgs e)
{
Debug.WriteLine("complete waiting thread");
}
我曾经和一个朋友在一起
您可以创建任意数量的任务,并将其添加到任务列表中。
工作进程将在添加任务时启动,如果在工作进程繁忙时添加任务,则重新启动,并在没有其他任务时停止
这将允许您在不冻结GUI的情况下,尽可能多地异步更新GUI
这对我来说是可行的
// 'tasks' is simply List<Task> that includes events for adding objects
private ObservableCollection<Task> tasks = new ObservableCollection<Task>();
// this will asynchronously iterate through the list of tasks
private BackgroundWorker task_worker = new BackgroundWorker();
public Form1()
{
InitializeComponent();
// set up the event handlers
tasks.CollectionChanged += tasks_CollectionChanged;
task_worker.DoWork += task_worker_DoWork;
task_worker.RunWorkerCompleted += task_worker_RunWorkerCompleted;
task_worker.WorkerSupportsCancellation = true;
}
// ----------- worker events
void task_worker_RunWorkerCompleted(object sender, RunWorkerCompletedEventArgs e)
{
if (tasks.Count != 0)
{
task_worker.RunWorkerAsync();
}
}
void task_worker_DoWork(object sender, DoWorkEventArgs e)
{
try
{
foreach (Task t in tasks)
{
t.RunSynchronously();
tasks.Remove(t);
}
}
catch
{
task_worker.CancelAsync();
}
}
// ------------- task event
// runs when a task is added to the list
void tasks_CollectionChanged(object sender,
System.Collections.Specialized.NotifyCollectionChangedEventArgs e)
{
if (!task_worker.IsBusy)
{
task_worker.RunWorkerAsync();
}
}
我知道这一点,代码片段演示了这一点。但是,问题是关于等待。我相信一个后台工作人员可以有多个正在运行的线程,你想等待哪一个线程?如果在另一个后台工作人员刚刚安排好时调用等待的代码会发生什么?我相信这就是这个问题不断出现的原因aring@Barfieldmv:这就是为什么AutoResetEvent是如此优雅的解决方案。您可以等待一个或多个任务,而不管它们运行在哪个线程上。对于多个任务,只需使用静态方法
AutoResetEvent.WaitAll(new[]{doneEvent1,doneEvent2})
@JohannesH要想等待多个事件,你必须确保它们触发并得到处理。我担心在UI线程上进行阻止会阻止windows窗体上的所有事件,这样你就可以永远等待。如果我没记错的话,backgroundworker会在后台线程上处理他的DowWork事件并触发WorkComplete如果主线程未被阻止,则在主线程上执行ted事件。至少对我来说,这进入了难以调试多线程的领域。@Barfieldmv:你是对的,这不像我想象的那么简单。但是,在我的示例中,我从DoWork处理程序调用DoneeEvent.Set(),因此至少
public class Program
{
public static int worker = 0;
public static object lockObject = 0;
static void Main(string[] args)
{
BackgroundworkerTest backgroundworkerTest = new BackgroundworkerTest();
backgroundworkerTest.WalkDir("C:\\");
while (backgroundworkerTest.Worker > 0)
{
// Exclusive write on console
lock (backgroundworkerTest.ExclusiveLock)
{
Console.CursorTop = 4; Console.CursorLeft = 1;
var consoleOut = string.Format("Worker busy count={0}", backgroundworkerTest.Worker);
Console.Write("{0}{1}", consoleOut, new string(' ', Console.WindowWidth-consoleOut.Length));
}
}
}
}
public class BackgroundworkerTest
{
private int worker = 0;
public object ExclusiveLock = 0;
public int Worker
{
get { return this.worker; }
}
public void WalkDir(string dir)
{
// Exclusive write on console
lock (this.ExclusiveLock)
{
Console.CursorTop = 1; Console.CursorLeft = 1;
var consoleOut = string.Format("Directory={0}", dir);
Console.Write("{0}{1}", consoleOut, new string(' ', Console.WindowWidth*3 - consoleOut.Length));
}
var currentDir = new System.IO.DirectoryInfo(dir);
DirectoryInfo[] directoryList = null;
try
{
directoryList = currentDir.GetDirectories();
}
catch (UnauthorizedAccessException unauthorizedAccessException)
{
// No access to this directory, so let's leave
return;
}
foreach (var directoryInfo in directoryList)
{
var bw = new BackgroundWorker();
bw.RunWorkerCompleted += (sender, args) =>
{
// Make sure that this worker variable is not messed up
lock (this.ExclusiveLock)
{
worker--;
}
};
DirectoryInfo info = directoryInfo;
bw.DoWork += (sender, args) => this.WalkDir(info.FullName);
lock (this.ExclusiveLock)
{
// Make sure that this worker variable is not messed up
worker++;
}
bw.RunWorkerAsync();
}
}
}
AutoResetEvent aevent = new AutoResetEvent(false);
private void button1_Click(object sender, EventArgs e)
{
bws = new BackgroundWorker();
bws.DoWork += new DoWorkEventHandler(bw_work);
bws.RunWorkerCompleted += new RunWorkerCompletedEventHandler(bw_complete);
bws.RunWorkerAsync();
bwWaiting.DoWork += new DoWorkEventHandler(waiting_work);
bwWaiting.RunWorkerCompleted += new RunWorkerCompletedEventHandler(waiting_complete);
bwWaiting.RunWorkerAsync();
}
void bw_work(object sender, DoWorkEventArgs e)
{
Thread.Sleep(2000);
}
void bw_complete(object sender, RunWorkerCompletedEventArgs e)
{
Debug.WriteLine("complete " + bwThread.ToString());
aevent.Set();
}
void waiting_work(object sender, DoWorkEventArgs e)
{
aevent.WaitOne();
}
void waiting_complete(object sender, RunWorkerCompletedEventArgs e)
{
Debug.WriteLine("complete waiting thread");
}
// 'tasks' is simply List<Task> that includes events for adding objects
private ObservableCollection<Task> tasks = new ObservableCollection<Task>();
// this will asynchronously iterate through the list of tasks
private BackgroundWorker task_worker = new BackgroundWorker();
public Form1()
{
InitializeComponent();
// set up the event handlers
tasks.CollectionChanged += tasks_CollectionChanged;
task_worker.DoWork += task_worker_DoWork;
task_worker.RunWorkerCompleted += task_worker_RunWorkerCompleted;
task_worker.WorkerSupportsCancellation = true;
}
// ----------- worker events
void task_worker_RunWorkerCompleted(object sender, RunWorkerCompletedEventArgs e)
{
if (tasks.Count != 0)
{
task_worker.RunWorkerAsync();
}
}
void task_worker_DoWork(object sender, DoWorkEventArgs e)
{
try
{
foreach (Task t in tasks)
{
t.RunSynchronously();
tasks.Remove(t);
}
}
catch
{
task_worker.CancelAsync();
}
}
// ------------- task event
// runs when a task is added to the list
void tasks_CollectionChanged(object sender,
System.Collections.Specialized.NotifyCollectionChangedEventArgs e)
{
if (!task_worker.IsBusy)
{
task_worker.RunWorkerAsync();
}
}
Task t = new Task(() => {
// do something here
});
tasks.Add(t);