在TaskCompletionSource.TrySetResult同步或异步延续? [英] Synchronous or asynchronous continuation upon TaskCompletionSource.TrySetResult?

查看:465
本文介绍了在TaskCompletionSource.TrySetResult同步或异步延续?的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

如何判断是否继续由启动 TaskCompletionSource.TrySetResult 将被同步或异步执行?

How to tell if the continuation initiated by TaskCompletionSource.TrySetResult is going to be executed synchronously or asynchronously?

例如:

// class A

void RegisterNotification(TaskCompletionSource<object> tcs)
{
    this.source.NotificationEvent += (s, eData) => 
    {
        Debug.WriteLine("A.before");
        tcs.TrySetResult(eData.Result);
        Debug.WriteLine("A.after");

        DoProcessingA();
    };   
}

// class B

async Task RequestNotificationAsync()
{
    var tcs = new TaskCompletionSource<object>();
    this.a.RegisterNotification(tcs);    

    Debug.WriteLine("B.before");
    var data = await tcs.Task;
    Debug.WriteLine("B.after");

    DoProcessingB();
}

如果 NotificationEvent 与来自哪里伺机tcs.Task 采取了不同的同步上下文一个线程被解雇到位,调试输出将是:

If NotificationEvent is fired on a thread with the different synchronization context from that of where await tcs.Task took place, the debug output will be:


B.before
A.before
A.after
B.after

也就是说,等待tcs.Task 延续异步执行。如果是在同一同步上下文解雇(或如果在这两个地方没有同步上下文),输出将是:

That is, the await tcs.Task continuation is executed asynchronously. If it is fired on the same synchronization context (or if there is no synchronization context in both places), the output will be:


B.before
A.before
B.after
A.after

也就是说,延续同步执行。

That is, the continuation is executed synchronously.

有没有办法来predict在 RegisterNotification

Is there a way to predict this order inside RegisterNotification?

我可以节省 SynchronizationContext.Current RegisterNotification 键,当我打电话以后比较一下 tcs.TrySetResult 。但是,这并不一定意味着等待tcs.Task 将于我救的上下文。

I could save SynchronizationContext.Current inside RegisterNotification and compare it later when I call tcs.TrySetResult. But that not necessarily mean that await tcs.Task will take place on the context I saved.

在理论上,如果我能predict这一点,我也许可以用它来诊断和prevent潜在的死锁。

In theory, if I could predict this, I might be able to use it to diagnose and prevent potential deadlocks.

推荐答案

我不认为这是一个记录的方式的predict同步/异步行为的setResult 提前。如果您要明确征收异步延续, Task.Run(()=&GT; tcs.SetResult())由@Damien_The_Unbeliever提出的想法很简单和普遍。

I don't think there is a documented way to predict synchronous/asynchronous behaviour of SetResult in advance. If you want to explicitly impose asynchronous continuation, the Task.Run(() => tcs.SetResult()) idea proposed by @Damien_The_Unbeliever is simple and universal.

然而,如果你的真正的想减少线程切换,仍然迫使异步性,你可以换 tcs.SetResult 使用自定义哑的SynchronizationContext 。相比于等待tcs.Task (以及该注册可能在任何其他的延续 TCS的背景下它的唯一目的是它的独特性。任务)。这将在 TaskCompletionSource 的消费者身边,因为异步调用(S),或者通过 SynchronizationContext.Post 或在游泳池螺纹(如果有关于消费者方面没有同步上下文)。

However, if you really would like to reduce thread switching and still force asynchrony, you could wrap tcs.SetResult with a custom dumb SynchronizationContext. The only purpose of it would be its uniqueness as compared to the context of await tcs.Task (and to that of any other continuations possibly registered on tcs.Task). That would cause asynchronous continuation(s) on the consumer side of TaskCompletionSource, either via SynchronizationContext.Post or on a pool thread (if there's no synchronization context on the consumer side).

测试程序:

using System;
using System.Diagnostics;
using System.Threading;
using System.Threading.Tasks;
using System.Windows.Forms;

namespace WinForms_21845495
{
    public partial class MainForm : Form
    {
        public MainForm()
        {
            InitializeComponent();

            this.Load += async (s, e) =>
            {
                // test on WindowsFormsSynchronizationContext
                await RequestNotificationAsync(notifyAsync: false);
                Debug.WriteLine(String.Empty);

                await RequestNotificationAsync(notifyAsync: true);
                Debug.WriteLine(String.Empty);

                // test on a pool thread
                await Task.Run(() => RequestNotificationAsync(notifyAsync: false));
                Debug.WriteLine(String.Empty);

                await Task.Run(() => RequestNotificationAsync(notifyAsync: true));
                Debug.WriteLine(String.Empty);
            };
        }

        async Task RegisterNotification(TaskCompletionSource<object> tcs, bool notifyAsync)
        {
            await Task.Delay(500);

            Debug.WriteLine("A.before");

            if (notifyAsync)
            {
                tcs.SetResultAsync(null);
            }
            else
            {
                tcs.SetResult(null);
            }

            Debug.WriteLine("A.after");
        }

        async Task RequestNotificationAsync(bool notifyAsync)
        {
            var tcs = new TaskCompletionSource<object>();
            var task = this.RegisterNotification(tcs, notifyAsync);

            Debug.WriteLine("B.before");

            var data = await tcs.Task;

            // do not yeild
            Thread.Sleep(500); 
            Debug.WriteLine("B.after");

            // yeild
            await Task.Delay(500); 
        }
    }

    public static class TaskExt
    {
        static public void SetResultAsync<T>(this TaskCompletionSource<T> tcs, T result)
        {
            FakeSynchronizationContext.Execute(() => tcs.SetResult(result));
        }

        // FakeSynchronizationContext
        class FakeSynchronizationContext : SynchronizationContext
        {
            private static readonly ThreadLocal<FakeSynchronizationContext> s_context =
                new ThreadLocal<FakeSynchronizationContext>(() => new FakeSynchronizationContext());

            private FakeSynchronizationContext() { }

            public static FakeSynchronizationContext Instance { get { return s_context.Value; } }

            public static void Execute(Action action)
            {
                var savedContext = SynchronizationContext.Current;
                SynchronizationContext.SetSynchronizationContext(FakeSynchronizationContext.Instance);
                try
                {
                    action();
                }
                finally
                {
                    SynchronizationContext.SetSynchronizationContext(savedContext);
                }
            }

            // SynchronizationContext methods

            public override SynchronizationContext CreateCopy()
            {
                return this;
            }

            public override void OperationStarted()
            {
                throw new NotImplementedException("OperationStarted");
            }

            public override void OperationCompleted()
            {
                throw new NotImplementedException("OperationCompleted");
            }

            public override void Post(SendOrPostCallback d, object state)
            {
                throw new NotImplementedException("Post");
            }

            public override void Send(SendOrPostCallback d, object state)
            {
                throw new NotImplementedException("Send");
            }
        }
    }
}

输出:


B.before
A.before
B.after
A.after

B.before
A.before
A.after
B.after

B.before
A.before
B.after
A.after

B.before
A.before
A.after
B.after

这篇关于在TaskCompletionSource.TrySetResult同步或异步延续?的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆