C# WPF异步等待任务锁定并行运行任务的UI线程
我有一个WPF应用程序,它在点击按钮时创建一个C# WPF异步等待任务锁定并行运行任务的UI线程,c#,wpf,multithreading,asynchronous,task-parallel-library,C#,Wpf,Multithreading,Asynchronous,Task Parallel Library,我有一个WPF应用程序,它在点击按钮时创建一个列表,并启动这些任务。我的假设是,Add()调用以并行但异步的方式启动它们 这是我的函数,它在远程计算机上串行执行一系列WMI调用: AgentBootstrapper.cs public async Task<int> BootstrapAsync(BootstrapContext context, IProgress<BootstrapAsyncProgress> progress) { ... do a bun
列表
,并启动这些任务。我的假设是,Add()
调用以并行但异步的方式启动它们
这是我的函数,它在远程计算机上串行执行一系列WMI调用:
AgentBootstrapper.cs
public async Task<int> BootstrapAsync(BootstrapContext context, IProgress<BootstrapAsyncProgress> progress)
{
...
do a bunch of stuff in serial *without* await calls
...
if (progress != null)
{
progress.Report(new BootstrapAsyncProgress
{
MachineName = context.MachineName,
ProgressPercentage = 30,
Text = "Copying install agent software to \\\\" + context.MachineName + "\\" + context.ShareName
});
}
...
return pid; // ProcessId of the remote agent that was just started
}
private async void InstallButton_Click(object sender, RoutedEventArgs e)
{
var bootstrapTasks = new List<Task<int>>();
var progress = new Progress<BootstrapAsyncProgress>();
progress.ProgressChanged += (o, asyncProgress) =>
{
Debug.WriteLine("{0}: {1}% {2}", asyncProgress.MachineName, asyncProgress.ProgressPercentage,
asyncProgress.Text);
//TODO Update ViewModel property for ProgressPercentage
};
var vm = DataContext as ShellViewModel;
Debug.Assert(vm != null);
foreach (var targetMachine in vm.TargetMachines)
{
var bootstrapContext = new BootstrapContext(targetMachine.MachineName, true)
{
AdminUser = vm.AdminUser,
AdminPassword = vm.AdminPassword
};
var bootstrapper = new AgentBootstrapper(bootstrapContext);
bootstrapTasks.Add(bootstrapper.BootstrapAsync(bootstrapContext, progress)); // UI thread locks up here
}
}
更新2
移动
等待任务WhenAll(任务)在foreach
循环之外的code>允许所有任务并行运行。在线程池上运行任务(即使用默认的任务调度程序)和等待任务。whalll(bootstrapTasks)
在UI线程中的线程上?为async
/wait
生成的代码中没有任何内容涉及线程的创建。使用async
关键字不会导致使用另一个线程。async
只允许您使用wait
关键字。如果您希望在另一个线程上发生某些事情,请尝试使用Task.Run
,因为Task.WaitAll()返回void,所以该线程不会编译。遗憾的是,这没有任何效果。它永远不会到达这条线。它被锁定在foreach循环中,执行Add()。不要只调用Bootstrap.Async-它会阻止,而是将其作为任务运行。如果您继续编辑答案,这些注释将毫无意义。我并不特别关心它是否位于不同的线程上。我就是不能锁定主线程。@MarkRichman我的观点是,如果你进行阻塞调用,它就是在UI线程上阻塞,因为你从未将任何东西移出该线程。您需要将所有串行阻塞代码放入一个操作中,并使用任务调用它。运行。谢谢,我认为这样做了。我将Add()
调用替换为这一行wait Task.Run(()=>bootstrapper.bootstrappasync(bootstrapContext,progress))代码>。用户界面没有锁定。那么,在这一点上,BootstrapAsync
甚至需要是一个异步方法吗?此外,这些进度事件将如何返回到其位于UI线程上的事件处理程序?@MarkRichman如果您从不等待,则它不需要是async
。进度事件将在UI线程上引发,因为progress
类捕获创建它的线程的SynchronizationContext
。它类似于BackgroundWorker
处理ReportProgress
的方式。在foreach循环中的int-pid=wait Task.Run(()=>bootstrapper.Bootstrap(bootstrapContext,progress))中的第一次迭代之后,它看起来仍然在阻塞代码>我将进度消息返回给处理程序,但其他任务不能并行运行。我必须这样做。我使用任务完成源并使用linq轮询列表。进度报告是通过每个任务附带的同步上下文完成的。@GarryVass我刚刚用更多信息更新了我的问题。您的方法仍然适用吗?@MarkRichman,仅供参考:@Noseratio可以,但是如果您等待任务。Run()
如何处理等待的任务?我需要将任务添加到我的列表中
。愚蠢的我-我只需要移动等待任务。whalll(任务)代码>在我的循环之外。作品
foreach (var targetMachine in vm.TargetMachines)
{
var tm = targetMachine; // copy closure variable
var bootstrapContext = new BootstrapContext(tm.MachineName, true)
{
AdminUser = vm.AdminUser,
AdminPassword = vm.AdminPassword
};
var bootstrapper = new AgentBootstrapper(bootstrapContext);
Debug.WriteLine("Starting Bootstrap task on default thread pool...");
var task = Task.Run(() =>
{
var pid = bootstrapper.Bootstrap(bootstrapContext, progress);
return pid;
});
Debug.WriteLine("Adding Task<int> " + task.Id + " to List<Task<int>>.");
tasks.Add(task);
await Task.WhenAll(tasks); // Don't proceed with the rest of this function untill all tasks are complete
}