Observe the following piece of code:
请注意以下代码:
var handler = GetTheRightHandler();
var bw = new BackgroundWorker();
bw.RunWorkerCompleted += OnAsyncOperationCompleted;
bw.DoWork += OnDoWorkLoadChildren;
bw.RunWorkerAsync(handler);
Now suppose I want to wait until bw
finishes working. What is the right way to do so?
现在假设我想等到bw完成工作。这样做的正确方法是什么?
My solution is this:
我的解决方案是:
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");
}
But I do not like it.
但我不喜欢它。
I have considered replacing the finished
flag with a synchronization event, set it in the RunWorkerCompleted
handler and block on it later instead of doing the while-sleep loop.
我已经考虑用同步事件替换完成的标志,在RunWorkerCompleted处理程序中设置它并在以后阻塞它而不是执行while-sleep循环。
Alas, it is wrong, because the code may run in the WPF or WindowsForm synchronization context, in which case I would block the same thread as the RunWorkerCompleted
handler runs on, which is clearly not very smart move.
唉,这是错误的,因为代码可能在WPF或WindowsForm同步上下文中运行,在这种情况下,我会阻止运行RunWorkerCompleted处理程序的同一个线程,这显然不是非常聪明的举动。
I would like to know of a better solution.
我想知道一个更好的解决方案。
Thanks.
谢谢。
EDIT:
编辑:
P.S.
附:
- The sample code is so contrived intentionally to clarify my question. I am perfectly aware of the completion callback and yet I want to know how to wait till completion. That is my question.
- 示例代码是故意设计的,以澄清我的问题。我完全知道完成回调,但我想知道如何等到完成。这是我的问题。
- I am aware of
Thread.Join
,Delegate.BeginInvoke
,ThreadPool.QueueUserWorkItem
, etc... The question is specifically aboutBackgroundWorker
. - 我知道Thread.Join,Delegate.BeginInvoke,ThreadPool.QueueUserWorkItem等...问题是关于BackgroundWorker。
EDIT 2:
编辑2:
OK, I guess it will be much easier if I explain the scenario.
好吧,我想如果我解释这个场景会更容易。
I have a unit test method, which invokes some asynchronous code, which in turn ultimately engages a BackgroundWorker
to which I am able to pass a completion handler. All the code is mine, so I can change the implementation if I wish to. I am not going, however, to replace the BackgroundWorker
, because it automatically uses the right synchronization context, so that when the code is invoked on a UI thread the completion callback is invoked on the same UI thread, which is very good.
我有一个单元测试方法,它调用一些异步代码,这反过来最终会使我能够传递完成处理程序的BackgroundWorker。所有的代码都是我的,所以如果我愿意,我可以改变实现。但是,我不会替换BackgroundWorker,因为它会自动使用正确的同步上下文,因此当在UI线程上调用代码时,会在同一UI线程上调用完成回调,这非常好。
Anyway, it is possible that the unit test method hits the end before the BW finishes its work, which is not good. So I wish to wait until the BW completes and would like to know the best way for it.
无论如何,单元测试方法有可能在BW完成其工作之前结束,这是不好的。所以我希望等到BW完成并想知道最好的方法。
There are more pieces to it, but the overall picture is more or less like I have just described.
它有更多的部分,但整体情况或多或少像我刚才描述的那样。
10 个解决方案
#1
41
Try using the AutoResetEvent class like this:
尝试使用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();
Caution: You should make sure that doneEvent.Set()
is called no matter what happens. Also you might want to provide the doneEvent.WaitOne()
with an argument specifying a timeout period.
警告:无论发生什么,都应该确保调用doneEvent.Set()。此外,您可能希望为doneEvent.WaitOne()提供指定超时期限的参数。
Note: This code is pretty much a copy of Fredrik Kalseth answer to a similar question.
注意:此代码几乎是Fredrik Kalseth对类似问题的回答。
#2
17
To wait for a background worker thread (single or multiple) do the following:
要等待后台工作线程(单个或多个),请执行以下操作:
-
Create a List of Background workers you have programatically created:
创建以编程方式创建的后台工作者列表:
private IList<BackgroundWorker> m_WorkersWithData = new List<BackgroundWorker>();
-
Add the background worker in the list:
在列表中添加后台worker:
BackgroundWorker worker = new BackgroundWorker(); worker.DoWork += new DoWorkEventHandler(worker_DoWork); worker.ProgressChanged += new ProgressChangedEventHandler(worker_ProgressChanged); worker.WorkerReportsProgress = true; m_WorkersWithData.Add(worker); worker.RunWorkerAsync();
-
Use the following function to wait for all workers in the List:
使用以下函数等待List中的所有worker:
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. }
#3
7
BackgroundWorker has a completion event. Instead of waiting, call your remaining code path from the completion handler.
BackgroundWorker有一个完成事件。而不是等待,从完成处理程序调用剩余的代码路径。
#4
2
This question is old but I don't think the author got the answer he was looking for.
这个问题很古老,但我不认为作者得到了他想要的答案。
This is a bit dirty, and it's in vb.NET but works for me
这有点脏,它在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
#5
2
VB.NET
While BackgroundWorker1.IsBusy()
Windows.Forms.Application.DoEvents()
End While
You can use this to chain multiple events. (sudo code to follow)
您可以使用它来链接多个事件。 (要跟随的sudo代码)
download_file("filepath")
While BackgroundWorker1.IsBusy()
Windows.Forms.Application.DoEvents()
End While
'Waits to install until the download is complete and lets other UI events function install_file("filepath")
While BackgroundWorker1.IsBusy()
Windows.Forms.Application.DoEvents()
End While
'Waits for the install to complete before presenting the message box
msgbox("File Installed")
#6
2
Checking backgrWorker.IsBusy
in the loop with Application.DoEvents()
is not a nicely way.
使用Application.DoEvents()检查循环中的backgrWorker.IsBusy并不是一个很好的方法。
I agree with @JohannesH, you should definitively use AutoResetEvent as a elegant solution. But not using it in UI Thread, it will cause main thread blocked; it should come from another background worker thread.
我同意@JohannesH,你应该明确地使用AutoResetEvent作为一个优雅的解决方案。但是不在UI Thread中使用它,它会导致主线程被阻塞;它应该来自另一个后台工作者线程。
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");
}
#7
0
not quite sure what u mean by waiting. Do you mean that you want something done (by the BW) after thats done you want to do something else? Use bw.RunWorkerCompleted like you do (use a seperate function for readability) and in that callback function do you next stuff. Start a timer to check if the work doesnt take too long.
不太确定你的意思是等待。你是说你想要完成其他事情之后想要完成某些事情(由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");
}
In the OnDoWorkLoadChildren:
在OnDoWorkLoadChildren中:
if ((worker.CancellationPending == true))
{
e.Cancel = true;
//return or something
}
#8
0
In OpenCV exists function WaitKey. Ir allows solve this issue in that way:
在OpenCV中存在函数WaitKey。 Ir允许以这种方式解决这个问题:
while (this->backgroundWorker1->IsBusy) {
waitKey(10);
std::cout << "Wait for background process: " << std::endl;
}
this->backgroundWorker1->RunWorkerAsync();
#9
0
I was also looking for a suitable solution. I solved the waiting with an exclusive lock. The critical path in code are writing to a public container (here the console) and increasing or decreasing the workers. No thread should interfere while writing to this variable, otherwise the count is not guaranteed anymore.
我也在寻找合适的解决方案。我用独家锁解决了等待。代码中的关键路径是写入公共容器(此处为控制台)并增加或减少工作人员。写入此变量时,没有线程会干扰,否则不再保证计数。
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();
}
}
}
#10
0
I used Tasks with a BackgroundWorker
我在BackgroundWorker中使用了Tasks
You can create any number of tasks and add them to a list of tasks. The worker will start when a task is added, restart if a task is added while the worker IsBusy, and stop once there are no more tasks.
您可以创建任意数量的任务并将其添加到任务列表中。工作人员将在添加任务时启动,如果在工作人员IsBusy中添加任务则重新启动,并在没有其他任务时停止。
This will allow you to update the GUI asynchronously as much as you need to without freezing it.
这将允许您根据需要异步更新GUI而不冻结它。
This works as is for me.
这适用于我。
// '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();
}
}
Now all you need is to create a new Task and add it to the List<>. It will be run by the worker in the order it was placed into the List<>
现在您只需要创建一个新任务并将其添加到List <>。它将由worker按照它放入List <>的顺序运行
Task t = new Task(() => {
// do something here
});
tasks.Add(t);
#1
41
Try using the AutoResetEvent class like this:
尝试使用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();
Caution: You should make sure that doneEvent.Set()
is called no matter what happens. Also you might want to provide the doneEvent.WaitOne()
with an argument specifying a timeout period.
警告:无论发生什么,都应该确保调用doneEvent.Set()。此外,您可能希望为doneEvent.WaitOne()提供指定超时期限的参数。
Note: This code is pretty much a copy of Fredrik Kalseth answer to a similar question.
注意:此代码几乎是Fredrik Kalseth对类似问题的回答。
#2
17
To wait for a background worker thread (single or multiple) do the following:
要等待后台工作线程(单个或多个),请执行以下操作:
-
Create a List of Background workers you have programatically created:
创建以编程方式创建的后台工作者列表:
private IList<BackgroundWorker> m_WorkersWithData = new List<BackgroundWorker>();
-
Add the background worker in the list:
在列表中添加后台worker:
BackgroundWorker worker = new BackgroundWorker(); worker.DoWork += new DoWorkEventHandler(worker_DoWork); worker.ProgressChanged += new ProgressChangedEventHandler(worker_ProgressChanged); worker.WorkerReportsProgress = true; m_WorkersWithData.Add(worker); worker.RunWorkerAsync();
-
Use the following function to wait for all workers in the List:
使用以下函数等待List中的所有worker:
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. }
#3
7
BackgroundWorker has a completion event. Instead of waiting, call your remaining code path from the completion handler.
BackgroundWorker有一个完成事件。而不是等待,从完成处理程序调用剩余的代码路径。
#4
2
This question is old but I don't think the author got the answer he was looking for.
这个问题很古老,但我不认为作者得到了他想要的答案。
This is a bit dirty, and it's in vb.NET but works for me
这有点脏,它在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
#5
2
VB.NET
While BackgroundWorker1.IsBusy()
Windows.Forms.Application.DoEvents()
End While
You can use this to chain multiple events. (sudo code to follow)
您可以使用它来链接多个事件。 (要跟随的sudo代码)
download_file("filepath")
While BackgroundWorker1.IsBusy()
Windows.Forms.Application.DoEvents()
End While
'Waits to install until the download is complete and lets other UI events function install_file("filepath")
While BackgroundWorker1.IsBusy()
Windows.Forms.Application.DoEvents()
End While
'Waits for the install to complete before presenting the message box
msgbox("File Installed")
#6
2
Checking backgrWorker.IsBusy
in the loop with Application.DoEvents()
is not a nicely way.
使用Application.DoEvents()检查循环中的backgrWorker.IsBusy并不是一个很好的方法。
I agree with @JohannesH, you should definitively use AutoResetEvent as a elegant solution. But not using it in UI Thread, it will cause main thread blocked; it should come from another background worker thread.
我同意@JohannesH,你应该明确地使用AutoResetEvent作为一个优雅的解决方案。但是不在UI Thread中使用它,它会导致主线程被阻塞;它应该来自另一个后台工作者线程。
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");
}
#7
0
not quite sure what u mean by waiting. Do you mean that you want something done (by the BW) after thats done you want to do something else? Use bw.RunWorkerCompleted like you do (use a seperate function for readability) and in that callback function do you next stuff. Start a timer to check if the work doesnt take too long.
不太确定你的意思是等待。你是说你想要完成其他事情之后想要完成某些事情(由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");
}
In the OnDoWorkLoadChildren:
在OnDoWorkLoadChildren中:
if ((worker.CancellationPending == true))
{
e.Cancel = true;
//return or something
}
#8
0
In OpenCV exists function WaitKey. Ir allows solve this issue in that way:
在OpenCV中存在函数WaitKey。 Ir允许以这种方式解决这个问题:
while (this->backgroundWorker1->IsBusy) {
waitKey(10);
std::cout << "Wait for background process: " << std::endl;
}
this->backgroundWorker1->RunWorkerAsync();
#9
0
I was also looking for a suitable solution. I solved the waiting with an exclusive lock. The critical path in code are writing to a public container (here the console) and increasing or decreasing the workers. No thread should interfere while writing to this variable, otherwise the count is not guaranteed anymore.
我也在寻找合适的解决方案。我用独家锁解决了等待。代码中的关键路径是写入公共容器(此处为控制台)并增加或减少工作人员。写入此变量时,没有线程会干扰,否则不再保证计数。
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();
}
}
}
#10
0
I used Tasks with a BackgroundWorker
我在BackgroundWorker中使用了Tasks
You can create any number of tasks and add them to a list of tasks. The worker will start when a task is added, restart if a task is added while the worker IsBusy, and stop once there are no more tasks.
您可以创建任意数量的任务并将其添加到任务列表中。工作人员将在添加任务时启动,如果在工作人员IsBusy中添加任务则重新启动,并在没有其他任务时停止。
This will allow you to update the GUI asynchronously as much as you need to without freezing it.
这将允许您根据需要异步更新GUI而不冻结它。
This works as is for me.
这适用于我。
// '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();
}
}
Now all you need is to create a new Task and add it to the List<>. It will be run by the worker in the order it was placed into the List<>
现在您只需要创建一个新任务并将其添加到List <>。它将由worker按照它放入List <>的顺序运行
Task t = new Task(() => {
// do something here
});
tasks.Add(t);