gpt4 book ai didi

c# - 如何检查 AsyncLocal 是否在同一个 "async context"内被访问

转载 作者:行者123 更新时间:2023-11-30 23:06:25 24 4
gpt4 key购买 nike

TL;DR ThreadLocal<T>.Value 指向相同的位置,如果 Thread.CurrentThread 保持不变。 AsyncLocal<T>.Value 是否有类似的东西(例如,SychronizationContext.CurrentExecutionContext.Capture() 是否适用于所有场景)?

想象一下,我们已经创建了一些数据结构的快照,该快照保存在线程本地存储(例如 ThreadLocal<T> 实例)中,并将其传递给辅助类以供以后使用。这个辅助类用于将这个数据结构恢复到快照状态。我们不想将此快照恢复到不同的线程上,因此我们可以检查创建了哪个线程辅助类。例如:

class Storage<T>
{
private ThreadLocal<ImmutableStack<T>> stackHolder;

public IDisposable Push(T item)
{
var bookmark = new StorageBookmark<T>(this);
stackHolder.Value = stackHolder.Value.Push(item);
return bookmark;
}

private class StorageBookmark<TInner> :IDisposable
{
private Storage<TInner> owner;
private ImmutableStack<TInner> snapshot;
private Thread boundThread;

public StorageBookmark(Storage<TInner> owner)
{
this.owner = owner;
this.snapshot = owner.stackHolder.Value;
this.boundThread = Thread.CurrentThread;
}

public void Dispose()
{
if(Thread.CurrentThread != boundThread)
throw new InvalidOperationException ("Bookmark crossed thread boundary");
owner.stackHolder.Value = snapshot;
}
}
}

有了这个,我们基本上将 StorageBookmark 绑定(bind)到特定线程,因此,绑定(bind)到 ThreadLocal 存储中特定版本的数据结构。我们通过在 Thread.CurrentThread 的帮助下确保我们不会跨越“线程上下文”来做到这一点
现在,手头的问题。我们如何使用 AsyncLocal<T> 而不是 ThreadLocal<T> 实现相同的行为?准确地说,是否有类似于 Thread.CurrentThread 的东西可以在构建和使用时进行检查,以控制“异步上下文”没有被跨越(这意味着 AsyncLocal<T>.Value 将指向与构建书签时相同的对象)。
似乎 SynchronizationContext.CurrentExecutionContext.Capture() 可能就足够了,但我不确定哪个更好,并且没有捕获(或者甚至可以在所有可能的情况下工作)

最佳答案

逻辑调用上下文与执行上下文具有相同的流语义,因此与 AsyncLocal 相同。知道了这一点,您可以在逻辑上下文中存储一个值以检测何时跨越“异步上下文”边界:

class Storage<T>
{
private AsyncLocal<ImmutableStack<T>> stackHolder = new AsyncLocal<ImmutableStack<T>>();

public IDisposable Push(T item)
{
var bookmark = new StorageBookmark<T>(this);

stackHolder.Value = (stackHolder.Value ?? ImmutableStack<T>.Empty).Push(item);
return bookmark;
}

private class StorageBookmark<TInner> : IDisposable
{
private Storage<TInner> owner;
private ImmutableStack<TInner> snapshot;
private Thread boundThread;
private readonly object id;

public StorageBookmark(Storage<TInner> owner)
{
id = new object();
this.owner = owner;
this.snapshot = owner.stackHolder.Value;
CallContext.LogicalSetData("AsyncStorage", id);
}

public void Dispose()
{
if (CallContext.LogicalGetData("AsyncStorage") != id)
throw new InvalidOperationException("Bookmark crossed async context boundary");
owner.stackHolder.Value = snapshot;
}
}
}

public class Program
{
static void Main()
{
DoesNotThrow().Wait();
Throws().Wait();
}

static async Task DoesNotThrow()
{
var storage = new Storage<string>();

using (storage.Push("hello"))
{
await Task.Yield();
}
}

static async Task Throws()
{
var storage = new Storage<string>();

var disposable = storage.Push("hello");

using (ExecutionContext.SuppressFlow())
{
Task.Run(() => { disposable.Dispose(); }).Wait();
}
}
}

关于c# - 如何检查 AsyncLocal<T> 是否在同一个 "async context"内被访问,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/48224335/

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