简体   繁体   English

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

[英]how can i force await to continue on the same thread?

await does not guarantee continuation on the same task for spawned tasks: 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);
    });
}

The output of this is: 这个输出是:

running on task 1
running on task

so we can see that not only the execution has moved to another task, but also to the UI-thread. 所以我们可以看到,不仅执行已经移动到另一个任务,而且还移动到UI线程。 How can i create a dedicated task, and enforce await to always continue on this task? 如何创建专用任务,并强制等待始终继续执行此任务? Long-running tasks don't do this either. 长时间运行的任务也不会这样做。

I have seen several SynchronizationContext implementations , but so far none of them worked, in this case because it uses threads and System.Threading.Thread is not available for uwp. 我已经看过几个SynchronizationContext实现 ,但到目前为止它们都没有工作,在这种情况下因为它使用线程而System.Threading.Thread不适用于uwp。

so we can see that not only the execution has moved to another task, but also to the UI-thread. 所以我们可以看到,不仅执行已经移动到另一个任务,而且还移动到UI线程。

No, it's not on the UI thread. 不,它不在UI线程上。 It's just technically not on a task, either. 从技术上讲,这也不是一项任务。 I explain why this happens in my blog post on Task.CurrentId in async methods . async方法的Task.CurrentId博客文章中解释了为什么会发生这种情况。

How can i create a dedicated task, and enforce await to always continue on this task? 如何创建专用任务,并强制等待始终继续执行此任务? Long-running tasks don't do this either. 长时间运行的任务也不会这样做。

You're on the right track: you need a custom SynchronizationContext (or a custom TaskScheduler ). 你走在正确的轨道上:你需要一个自定义的SynchronizationContext (或一个自定义的TaskScheduler )。

I have seen several SynchronizationContext implementations, but so far none of them worked, in this case because it uses threads and System.Threading.Thread is not available for uwp. 我已经看过几个SynchronizationContext实现,但到目前为止它们都没有工作,在这种情况下因为它使用线程而System.Threading.Thread不适用于uwp。

Try out mine . 试试我的 It should work on UWP 10.0. 它应该适用于UWP 10.0。

Don't use Task.Run , just make the event handler async 不要使用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");
 }

Read more about Task.CurrentId in Async Methods here : 此处阅读有关Async方法中的Task.CurrentId更多信息:

So Task.CurrentId returns null because there is no task actually executing. 所以Task.CurrentId返回null因为没有任务实际执行。

Reply to comments 回复评论

  1. " It still runs on the UI thread, and not a spawned task. " 它仍然在UI线程上运行,而不是生成的任务。

The case with a thread pool thread is included in the link. 具有线程池线程的情况包含在链接中。 In particular look at this example 特别要看这个例子

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

Again, the clarification is 再一次,澄清是

the null comes into play because the async method is first executed as an actual task on the thread pool. null发挥作用,因为async方法首先作为线程池上的实际任务执行。 However, after its await , it resumes as a regular delegate on the thread pool ( not an actual task ). 但是,在await ,它将恢复为线程池上的常规委托( 而不是实际任务 )。

  1. " my question is how to stop it from doing that " 我的问题是如何阻止它这样做

This is an implementation detail of the async call, I can only quote the link again: 这是async调用的实现细节,我只能再次引用该链接:

It's likely that this behavior is just the result of the easiest and most efficient implementation. 这种行为可能只是最简单,最有效的实施的结果。

So you can't and you should not stop it from doing that, as far as a truly async call is concerned. 因此,就真正的 async调用而言,你不能也不应该阻止它这样做。

What you describe as the expected behavior instead is equivalent to a Task.Run without await 您所描述的预期行为相当于没有 awaitTask.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);
         Task.Delay(TimeSpan.FromMilliseconds(100)).Wait();
         Console.WriteLine("After - running on task {0} {1}", 
            Task.CurrentId.HasValue ? Task.CurrentId.ToString() : "null",
            Environment.CurrentManagedThreadId);
     });
 }

Or a nested Task.Run 或嵌套的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);
     });
 }

Output 产量

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

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

相关问题 在等待在同一个线程上执行之后如何获得延续? - How can I get the continuation after an await to execute on the same thread? 有没有办法强制async / await继续第一个可用的线程? - Is there a way to force async/await to continue on the first available thread? 我可以强制ClassCleanup与ClassInitialize在同一线程中执行吗? - Can I force ClassCleanup to execute in same thread as ClassInitialize? 强制任务继续在当前线程上执行? - Force a Task to continue on the current thread? 如何在WCF线程中强制未处理的异常使进程崩溃? - How can I force unhandled exceptions in a WCF thread to crash the process? 我认为 await 继续与调用者在同一线程上,但似乎没有 - I thought await continued on the same thread as the caller, but it seems not to 如何将其转换为异步等待代码并仅使用1个线程? - How can I convert this to async-await code and use only 1 thread? 如何强制Resharper将花括号放在同一行上? - How can I force Resharper to place the curly brace on the same line? 使用“async/await”时可以获取 UI 线程上下文吗 - Can I get UI thread context while using 'async/await' 如何并行执行嵌套的异步/等待代码,同时在等待延续上保持相同的线程? - How to execute nested async/await code in parallel while maintaining the same thread on await continuations?
 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM