UI线程上的任务继续

Gre*_*som 205 .net c# wpf multithreading task

是否有一种"标准"方式来指定任务延续应该在创建初始任务的线程上运行?

目前我有下面的代码 - 它正在工作,但跟踪调度程序和创建第二个动作似乎是不必要的开销.

dispatcher = Dispatcher.CurrentDispatcher;
Task task = Task.Factory.StartNew(() =>
{
    DoLongRunningWork();
});

Task UITask= task.ContinueWith(() =>
{
    dispatcher.Invoke(new Action(() =>
    {
        this.TextBlock1.Text = "Complete"; 
    }
});
Run Code Online (Sandbox Code Playgroud)

Gre*_*som 336

用以下方式调用延续TaskScheduler.FromCurrentSynchronizationContext():

    Task UITask= task.ContinueWith(() =>
    {
     this.TextBlock1.Text = "Complete"; 
    }, TaskScheduler.FromCurrentSynchronizationContext());
Run Code Online (Sandbox Code Playgroud)

仅当当前执行上下文位于UI线程上时,这才适用.

  • 仅当当前执行上下文位于UI线程上时,它才有效.如果您将此代码放在另一个Task中,那么您将获得InvalidOperationException(请参阅[Exceptions](http://msdn.microsoft.com/en-us/library/system.threading.tasks.taskscheduler.fromcurrentsynchronizationcontext(v = vs. 110).aspx)部分) (38认同)
  • 在.NET 4.5中,Johan Larsson的答案应该用作UI线程上任务延续的标准方法.只需写:await Task.Run(DoLongRunningWork); this.TextBlock1.Text ="完成"; 另见:http://blogs.msdn.com/b/pfxteam/archive/2011/10/24/10229468.aspx (3认同)
  • 谢谢你救了我的命。我花了几个小时来弄清楚如何在等待/继续中调用主线程。对于其他人如何使用 [Google Firebase SDK for Unity](https://firebase.google.com/docs/unity/setup) 并且仍然存在相同的问题,这是一种可行的方法。 (2认同)
  • @MarcelW - `await` 是一个很好的模式 - 但前提是您位于 `async` 上下文中(例如声明为 `async` 的方法)。如果没有,仍然有必要做类似这个答案的事情。 (2认同)

Joh*_*son 31

使用异步你只需:

await Task.Run(() => do some stuff);
// continue doing stuff on the same context as before.
// while it is the default it is nice to be explicit about it with:
await Task.Run(() => do some stuff).ConfigureAwait(true);
Run Code Online (Sandbox Code Playgroud)

然而:

await Task.Run(() => do some stuff).ConfigureAwait(false);
// continue doing stuff on the same thread as the task finished on.
Run Code Online (Sandbox Code Playgroud)

  • 'false`版本下的评论让我感到困惑.我认为`false`意味着它可能继续在*不同的*线程上. (2认同)
  • 该问题没有指定位于“async”方法内部(这是使用“await”所必需的)。当“await”不可用时,答案是什么? (2认同)

Sim*_*ver 21

如果您有需要发送到UI的返回值,则可以使用如下通用版本:

在我的例子中,这是从MVVM ViewModel调用的.

var updateManifest = Task<ShippingManifest>.Run(() =>
    {
        Thread.Sleep(5000);  // prove it's really working!

        // GenerateManifest calls service and returns 'ShippingManifest' object 
        return GenerateManifest();  
    })

    .ContinueWith(manifest =>
    {
        // MVVM property
        this.ShippingManifest = manifest.Result;

        // or if you are not using MVVM...
        // txtShippingManifest.Text = manifest.Result.ToString();    

        System.Diagnostics.Debug.WriteLine("UI manifest updated - " + DateTime.Now);

    }, TaskScheduler.FromCurrentSynchronizationContext());
Run Code Online (Sandbox Code Playgroud)


Dea*_*ean 12

我只是想添加这个版本,因为这是一个非常有用的线程,我认为这是一个非常简单的实现.如果多线程应用程序我在各种类型中多次使用它:

 Task.Factory.StartNew(() =>
      {
        DoLongRunningWork();
        Application.Current.Dispatcher.BeginInvoke(DispatcherPriority.Normal, new Action(() =>
              { txt.Text = "Complete"; }));
      });
Run Code Online (Sandbox Code Playgroud)

  • 只想加强我的最后评论:开发人员不仅要存储同步上下文,而且他/她必须知道这只能从主线程获得; 这个问题一直是数十个SO问题混淆的原因:人们一直试图从工作线程中获得这个问题.如果他们的代码本身已被移动到工作线程中,则由于此问题而失败.因此,由于WPF的流行,这一点在这个流行的问题中肯定应该澄清. (3认同)
  • 不是低估,因为在某些情况下这是一个可行的解决方案; 然而,接受的答案更好.它与技术无关(`TaskScheduler`是BCL的一部分,`Dispatcher`不是)并且可以用来组成复杂的任务链,因为它不必担心任何即发即弃的异步操作(例如`BeginInvoke `). (2认同)

Dbl*_*Dbl 5

通过谷歌来到这里,因为我正在寻找一种在 Task.Run 调用后在 ui 线程上执行操作的好方法 - 使用以下代码,您可以使用它await再次返回到 UI 线程。

我希望这可以帮助别人。

public static class UI
{
    public static DispatcherAwaiter Thread => new DispatcherAwaiter();
}

public struct DispatcherAwaiter : INotifyCompletion
{
    public bool IsCompleted => Application.Current.Dispatcher.CheckAccess();

    public void OnCompleted(Action continuation) => Application.Current.Dispatcher.Invoke(continuation);

    public void GetResult() { }

    public DispatcherAwaiter GetAwaiter()
    {
        return this;
    }
}
Run Code Online (Sandbox Code Playgroud)

用法:

public static class UI
{
    public static DispatcherAwaiter Thread => new DispatcherAwaiter();
}

public struct DispatcherAwaiter : INotifyCompletion
{
    public bool IsCompleted => Application.Current.Dispatcher.CheckAccess();

    public void OnCompleted(Action continuation) => Application.Current.Dispatcher.Invoke(continuation);

    public void GetResult() { }

    public DispatcherAwaiter GetAwaiter()
    {
        return this;
    }
}
Run Code Online (Sandbox Code Playgroud)