Calling TaskCompletionSource.SetResult in a non blocking manner

前端 未结 4 2022
夕颜
夕颜 2020-12-03 06:59

I\'ve discovered that TaskCompletionSource.SetResult(); invokes the code awaiting the task before returning. In my case that result in a deadlock.

This

4条回答
  •  爱一瞬间的悲伤
    2020-12-03 07:26

    A little late to the party, but here's my solution which i think is added value.

    I've been struggling with this also, i've solved it by capturing the SynchronizationContext on the method that is awaited.

    It would look something like:

    // just a default sync context
    private readonly SynchronizationContext _defaultContext = new SynchronizationContext();
    
    void ReceiverRun()
    {
        while (true)    // <-- i would replace this with a cancellation token
        {
            var msg = ReadNextMessage();
            TaskWithContext task = requests[msg.RequestID];
    
            // if it wasn't a winforms/wpf thread, it would be null
            // we choose our default context (threadpool)
            var context = task.Context ?? _defaultContext;
    
            // execute it on the context which was captured where it was added. So it won't get completed on this thread.
            context.Post(state =>
            {
                if (msg.Error == null)
                    task.TaskCompletionSource.SetResult(msg);
                else
                    task.TaskCompletionSource.SetException(new Exception(msg.Error));
            });
        }
    }
    
    public static Task SendAwaitResponse(string msg)
    {
        // The key is here! Save the current synchronization context.
        var t = new TaskWithContext(SynchronizationContext.Current); 
    
        requests.Add(GetID(msg), t);
        stream.Write(msg);
        return t.TaskCompletionSource.Task;
    }
    
    // class to hold a task and context
    public class TaskWithContext
    {
        public SynchronizationContext Context { get; }
    
        public TaskCompletionSource TaskCompletionSource { get; } = new TaskCompletionSource();
    
        public TaskWithContext(SynchronizationContext context)
        {
            Context = context;
        }
    }
    

提交回复
热议问题