c# - 在 dispose 上更改线程静态值不适用于异步
问题描述
可能是以代码开头的代码会更容易提出问题:
public class RecorderScope : IDisposable
{
[ThreadStatic]
private static RecorderScope current;
public static RecorderScope Current => current;
public RecorderScope()
{
if (current != null) throw new Exception("usually by design, don't make complex stuff");
current = this;
}
public void Dispose()
{
current = null;
}
}
意思是,其他一些类可能会检查它是否在 RecorderScope 中使用并根据该事实做一些事情。它仅用于单元测试,用于简单的情况,所以我不关心嵌套等 - 但单元测试可能会并行执行,因此[ThreadStatic]
.
现在只要在该范围内没有等待,它就可以正常工作。我做了一些日志记录,看起来问题是构造函数带有 id x 的线程,而 dispose 带有 id y 的线程(正如预期的那样,这就是异步的工作方式),因此线程的 x 值仍然没有被释放。
任何想法如何解决它?
解决方案
现在只要在该范围内没有等待它就可以正常工作......有什么想法可以解决它吗?
是的。你真的不应该ThreadStatic
在现代代码中使用。改用AsyncLocal<T>
:
public class RecorderScope : IDisposable
{
private static AsyncLocal<RecorderScope> current;
public static RecorderScope Current => current.Value;
public RecorderScope()
{
if (Current != null) throw new Exception("usually by design, don't make complex stuff");
Current.Value = this;
}
public void Dispose()
{
Current.Value = null;
}
}
我有一篇博客文章涉及更多语义细节(在AsyncLocal<T>
存在之前写过)。
一个没有很好记录的重要说明AsyncLocal<T>
是,您应该始终通过设置Value
属性来更新值 - 永远不要通过修改T
对象。理想情况下,T
应该是不可变的,但在这种特殊情况下(因为Value
只设置 when null
,后来只设置为null
,从不更新),不可变性不是必需的。