gpt4 book ai didi

c# - TaskCompletionSource.TrySetResult 上的同步或异步继续?

转载 作者:太空狗 更新时间:2023-10-30 01:19:48 25 4
gpt4 key购买 nike

如何判断 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.beforeA.beforeA.afterB.after

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

B.beforeA.beforeB.afterA.after

即continuation是同步执行的。

有没有办法在 RegisterNotification 中预测这个顺序?

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

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

最佳答案

我认为没有记录在案的方法来提前预测 SetResult 的同步/异步行为。如果你想显式地强加异步延续,@Damien_The_Unbeliever 提出的 Task.Run(() => tcs.SetResult()) 想法简单而通用。

但是,如果您真的想减少线程切换并仍然强制异步,您可以用自定义的 SynchronizationContext 包装 tcs.SetResult >。与 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.beforeA.beforeB.afterA.afterB.beforeA.beforeA.afterB.afterB.beforeA.beforeB.afterA.afterB.beforeA.beforeA.afterB.after

关于c# - TaskCompletionSource.TrySetResult 上的同步或异步继续?,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/21845495/

25 4 0
Copyright 2021 - 2024 cfsdn All Rights Reserved 蜀ICP备2022000587号
广告合作:1813099741@qq.com 6ren.com