首页 > 解决方案 > 在 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 值仍然没有被释放。

任何想法如何解决它?

标签: c#.net-core

解决方案


现在只要在该范围内没有等待它就可以正常工作......有什么想法可以解决它吗?

是的。你真的不应该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,从不更新),不可变性不是必需的。


推荐阅读