簡體   English   中英

WPF異步等待任務鎖定UI線程並行運行任務

[英]WPF async await Task Locks UI Thread Running Tasks in Parallel

我有一個WPF應用程序,按下按鈕后,創建一個List<Task<int>>並啟動這些任務。 我的假設是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
}

這顯然是UI中的按鈕處理程序:

Shell.xaml.cs

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
    }
}

我知道標記為async函數應該使用await在其中進行函數調用。 在我的例子中,這些都是對一些同步WMi輔助函數的調用,這些函數都返回void 所以,我不認為await是我想要的。

簡單地說,我想要所有bootstrapTasks項目(對bootstrapper.BootstrapAsync()的調用立即觸發,並讓UI線程從所有這些項目接收進度事件。當整個批次完成后,我將需要處理太。

更新1

嘗試使用Task.Run()修復了UI鎖定問題,但只執行了第一個Task實例。 更新foreach循環:

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
}

更新2

移動await Task.WhenAll(tasks); foreach循環外部允許所有任務並行運行。

async / await生成的代碼中沒有任何內容涉及線程的創建。 使用async關鍵字不會導致使用另一個線程。 所有async都允許您使用await關鍵字。 如果您希望在另一個線程上發生某些事情,請嘗試使用Task.Run

在線程池中運行任務(使用默認任務調度程序,即)並在UI線程中await Task.WhenAll(bootstrapTasks)

暫無
暫無

聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.

 
粵ICP備18138465號  © 2020-2024 STACKOOM.COM