TaskCompletionSource.TrySetResult上的同步或异步延续?

时间:2014-02-18 05:59:33

标签: c# multithreading asynchronous task-parallel-library async-await

如何判断TaskCompletionSource.TrySetResult启动的延续是同步执行还是异步执行?

例如:

// 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的同步上下文不同的同步上下文的线程上触发await tcs.Task,则调试输出将为:

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

也就是说,await tcs.Task延续是异步执行的。如果在同一个同步上下文中触发(或者两个地方都没有同步上下文),则输出为:

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

也就是说,继续是同步执行的。

有没有办法在RegisterNotification内预测此订单?

我可以将SynchronizationContext.Current保存在RegisterNotification内,稍后在我致电tcs.TrySetResult时进行比较。但这并不一定意味着await tcs.Task将在我保存的上下文中发生。

理论上,如果我可以预测到这一点,我或许可以用它来诊断和防止潜在的死锁。

1 个答案:

答案 0 :(得分:4)

我认为没有一种文档化的方法可以提前预测SetResult的同步/异步行为。如果你想明确强加异步延续,那么@Damien_The_Unbeliever提出的Task.Run(() => tcs.SetResult())想法是简单而通用的。

但是,如果确实希望减少线程切换并仍然强制异步,则可以使用自定义哑tcs.SetResult包裹SynchronizationContext。与await tcs.Task的上下文(以及可能在tcs.Task上注册的任何其他延续的上下文)相比,它的唯一目的是它的唯一性。这将导致TaskCompletionSource的消费者端的异步延续,通过SynchronizationContext.Post或池线程(如果消费者端没有同步上下文)。

测试应用:

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