在启动另一个后台工作线程中的新任务之前如何等待后台工作线程任务完成?



我最初的问题是关于UI阻塞,我有两个外部进程,应该与其他一些代码连续执行,每个进程将执行n times,这些进程的两个执行导致UI冻结,为了避免这个问题我backgroundworker对象实现的代码,第一个过程ProcessA将在后台worker中,第二个ProcessB将在后台工作。

让我们从ProcessA开始,当我运行应用程序并开始执行整个任务时,ProcessA将运行这么快并输出结果(我不知道为什么它运行得这么快),对于结果,它们似乎是正确的。

ProcessA执行n步,每一步,它将创建一个新的BackgroundWorker来完成任务,并在后台执行任务。

第二个进程必须在第一个进程完成后执行,我现在的问题是第一个进程的CompletedTask事件不会执行,第二个进程在第一个进程完成之前开始。

public void ButtonClick(object sender, RoutedEventArgs e)
{
for (int i = 0; i < steps + 1; i++)
{
backgroundworker = new BackgroundWorker();
backgroundworker.DoWork += new DoWorkEventHandler(BackgroundWorker_DoWork);
backgroundworker.RunWorkerCompleted += BackgroundWorker_RunWorkerCompleted;
backgroundworker.RunWorkerAsync(Tuple.Create(mypath, i));
}
While(!FirstProcessFinished)
Thread.Sleep(1000);
RunSecondProcess(mypath);
}

protected void BackgroundWorker_DoWork(object sender, DoWorkEventArgs e)
{
// Some code to run first Process
}

protected void BackgroundWorker_RunWorkerCompleted(object sender, RunWorkerCompletedEventArgs e)
{
Counter++;
IncrementProgressBar();
Thread.Sleep();
Console.WriteLine("Finished");
if (counter == Steps + 1)
{
FirstProcessFinished = true;
}
}

如何执行ProcessB正确调用n次backgroundWorker_Completed?

更新:

我已经测试了Orace答案的解决方案,但是现在我不能执行第二个任务,第一个任务也不能完成这个过程:

private async void Work(string path)
{
try
{
// start all A tasks
var aTasks = new List<Task>();
for (int i = 0; i < steps; i++)
{
aTasks.Add(new Task(() => RunA(path, i)));
}

// wait for them to end
await Task.WhenAll(aTasks);
IncrementProgressBar();
// start B task (it will not be allowed to update the UI)
await Task.Run(() => RunB(path));
IncrementProgressBar();
}
catch (Exception e)
{
Console.WriteLine(e);
throw;
}
}
private async Task RunA(string path, int index)
{
// start the actual work (new task to not freeze the UI)
await Task.Run(() => WorkA(path, index));
// here we are back on the UI thread so we can update the status
IncrementProgressBar();
Console.WriteLine($"Task A{index:00} Finished");
}

怎么了??

Task,asyncawait是可行的。下面是你可以写的一个例子:

using System;
using System.Linq;
using System.Threading.Tasks;
//------------------------
// async void should be used with caution.
// see: https://learn.microsoft.com/en-us/archive/msdn-magazine/2013/march/async-await-best-practices-in-asynchronous-programming#avoid-async-void
// see: https://johnthiriet.com/mvvm-going-async-with-async-command/
public async void ButtonClick(object sender, RoutedEventArgs e)
{
try
{
// start all A tasks
var aTasks = Enumerable.Range(0, steps + 1).Select(i => RunA(myPath, i));
// wait for them to end
await Task.WhenAll(aTasks);
// start a new task for B
// you can't update the UI from the code inside RunB
// because it will probably run on a different thread
await Task.Run(() => RunB(myPath));
// you can update the UI here
FillProgressBar();
}
catch (Exception e)
{
Console.WriteLine(e);
throw;
}
}
private async Task RunA(string path, int index)
{
// start a new task for the actual work performed by A
// you can't update the UI from the code inside WorkA
// because it will probably run on a different thread
await Task.Run(() => WorkA(path, index));
// here the magic happen.
// We are back on the UI thread so we can update the status
IncrementProgressBar();
Console.WriteLine($"Task A{index:00} Finished");
}
private void WorkA(string path, int index)
{
// the work
}
private void RunB(string path)
{
// the work
}

最新更新