我怎么能强制等待继续在同一个线程?

时间:2017-03-13 08:45:06

标签: c# asynchronous task-parallel-library synchronizationcontext

await不保证在生成任务的同一任务上继续:

private void TestButton_Click(object sender, RoutedEventArgs e)
{
    Task.Run(async () =>
    {
        Debug.WriteLine("running on task " + Task.CurrentId);
        await Task.Delay(TimeSpan.FromMilliseconds(100));
        Debug.WriteLine("running on task " + Task.CurrentId);
    });
}

这个输出是:

running on task 1
running on task

所以我们可以看到,不仅执行已经移动到另一个任务,而且还移动到UI线程。如何创建专用任务,并强制等待始终继续执行此任务?长时间运行的任务也不会这样做。

我见过几个SynchronizationContext implementations,但到目前为止它们都没有用,在这种情况下因为它使用线程而且System.Threading.Thread不适用于uwp。

2 个答案:

答案 0 :(得分:9)

  

所以我们可以看到,不仅执行已经移动到另一个任务,而且还移动到UI线程。

不,它不在UI线程上。它在技术上也不是一项任务。我在Task.CurrentId in async methods的博文中解释了为什么会这样。

  

如何创建专用任务,并强制等待始终继续执行此任务?长期执行的任务也不会这样做。

您已走上正轨:您需要自定义SynchronizationContext(或自定义TaskScheduler)。

  

我已经看过几个SynchronizationContext实现,但到目前为止它们都没有工作,在这种情况下因为它使用线程而且System.Threading.Thread不适用于uwp。

试试mine。它应该适用于UWP 10.0。

答案 1 :(得分:2)

不要使用Task.Run,只需将事件处理程序设为异步

即可
 private async void TestButton_Click(object sender, RoutedEventArgs e)
 {
     await dedicated();
 }

 private async Task dedicated()
 {
     Console.WriteLine("running on task {0}", Task.CurrentId.HasValue ? Task.CurrentId.ToString() : "null");
     await Task.Delay(TimeSpan.FromMilliseconds(100));
     Console.WriteLine("running on task {0}", Task.CurrentId.HasValue ? Task.CurrentId.ToString() : "null");
 }

详细了解异步方法here中的Task.CurrentId

  

所以Task.CurrentId返回null,因为实际上没有任务   执行。

回复评论

  1. 它仍然在UI线程上运行,而不是在生成的任务中运行。
  2. 链接中包含线程池线程的情况。特别要看这个例子

    static void Main(string[] args)
    {
        var task = Task.Run(() => MainAsync());
        task.Wait();
        taskRun = task.Id.ToString();
    
        Console.WriteLine(beforeYield + "," + afterYield + "," + taskRun);
        Console.ReadKey();
    }
    
    static async Task MainAsync()
    {
        beforeYield = Task.CurrentId.HasValue ? Task.CurrentId.ToString() : "null";
        await Task.Yield();
        afterYield = Task.CurrentId.HasValue ? Task.CurrentId.ToString() : "null";
    }
    

    同样,澄清是

      

    null开始发挥作用,因为async方法首先被执行为   线程池上的实际任务。但是,在它await之后   恢复为线程池中的常规委托(不是实际任务)。

    1. 我的问题是如何阻止它
    2. 这是async电话的实施细节,我只能再次引用该链接:

        

      这种行为很可能只是最简单的结果   最有效的实施。

      所以,就真正的 async电话而言,你不能也不应该阻止它这样做。

      您所描述的预期行为相当于Task.Run 没有 await

       private void expected()
       {
           Task task = Task.Run(() =>
           {
               Console.WriteLine("Before - running on task {0} {1}", 
                  Task.CurrentId.HasValue ? Task.CurrentId.ToString() : "null",
                  Environment.CurrentManagedThreadId);
               Task.Delay(TimeSpan.FromMilliseconds(100)).Wait();
               Console.WriteLine("After - running on task {0} {1}", 
                  Task.CurrentId.HasValue ? Task.CurrentId.ToString() : "null",
                  Environment.CurrentManagedThreadId);
           });
       }
      

      或嵌套Task.Run

       private void expected()
       {
           Task task = Task.Run(() =>
           {
               Console.WriteLine("Before - running on task {0} {1}",
                   Task.CurrentId.HasValue ? Task.CurrentId.ToString() : "null",
                   Environment.CurrentManagedThreadId);
               var inner = Task.Run( async () =>
                   await Task.Delay(TimeSpan.FromMilliseconds(100)));
               inner.Wait();
               Console.WriteLine("After - running on task {0} {1}",
                   Task.CurrentId.HasValue ? Task.CurrentId.ToString() : "null",
                   Environment.CurrentManagedThreadId);
           });
       }
      

      输出

      Before - running on task 312 11
      After - running on task 312 11
      Before - running on task 360 11
      After - running on task 360 11
      Before - running on task 403 15
      After - running on task 403 15