【问题标题】:ThreadStatic for TPL TaskTPL 任务的 ThreadStatic
【发布时间】:2011-08-01 09:45:04
【问题描述】:

如何在 TPL 任务中使用 ThreadStatic 之类的东西?我的理解(“Wrox Professional Parallel Programming with C#”,p74)是任务可以在执行期间从一个线程切换到另一个线程。

我想做什么?

我想在静态类中维护一个会话 ID,因此我不需要将此 ID 传递给我的所有方法。我的库有 login(id)logout(id) 之类的方法以及许多对与此 ID 关联的凭据进行操作的方法。但我不想将此 id 传递给每个方法。我可以确保在不同的线程中为不同的会话调用我的库。因此,将 login() 中的 id 保存在 ThreadStatic 变量中即可。

现在我想使用由ThreadPool 为我创建的 TPL 任务。我可以将我的会话 ID 传递给任务,但是如果我将此 ID 存储在 ThreadStatic 变量中,那么如果我的任务切换线程,它将无法生存。

【问题讨论】:

  • 我没有听说过实际的task 在执行期间从一个线程切换到另一个线程。您是从哪里听说这种可能性的?
  • 《Wrox Professional Parallel Programming with C#》一书的第 74 页有一张图显示了工作线程之间的任务切换。
  • @Gerard:你确定这是 while execution 而不是 queue 中的一个线程的任务,然后工作窃取意味着它切换转到另一个线程执行?
  • 该图清楚地显示了一个运行时间较长的任务,该任务在执行期间切换线程。我也找不到任何其他说明线程切换理论的资源。我希望这个数字是错误的,并且 startet 任务肯定附加到一个线程。
  • 我同意即使有可能在线程之间迁移任务,也不太可能实现:这样做成本太高。即使是这种情况,您对在不同任务之间共享相同 ID 的想法感到满意吗?很可能,任务将在同一个线程上多路复用。您提到的解决方案(将 ID 传递给每个方法)对我来说似乎是最干净的。

标签: c# .net multithreading


【解决方案1】:

TPL 和 .Net 4.5 的异步流是 ExecutionContext,这意味着您可以像使用 ThreadStatic 一样使用 CallContext.LogicalSetData(string, object)CallContext.GetLogicalData(string)。但是,它确实会导致显着的性能损失。这已在 .Net 4.6 及更高版本(包括 .Net Standard 1.3 及更高版本)中以the AsyncLocal<> wrapper 公开。

请参阅 Async Causality Chain TrackingHow to include own data in ExecutionContextExecutionContext vs SynchronizationContext 了解更深入的了解。

使用示例:

class Program
{
    static async void Main(string[] args)
    {
        Logger.Current = new Logger("Test Printer");

        Logger.Current.Print("hello from main");
        await Task.Run(() => Logger.Current.Print($"hello from thread {Thread.CurrentThread.ManagedThreadId}"));
        await Task.Run(() => Logger.Current.Print($"hello from thread {Thread.CurrentThread.ManagedThreadId}"));
    }
}

class Logger
{
    private string LogName;

    public Logger(string logName)
    {
        if (logName == null)
            throw new InvalidOperationException();

        this.LogName = logName;
    }

    public void Print(string text)
    {
        Console.WriteLine(LogName + ": " + text);
    }

    private static AsyncLocal<Logger> _logger = new AsyncLocal<Logger>();
    public static Logger Current
    {
        get => _logger.Value;
        set => _logger.Value = value;
        }
    }
}

打印:

测试打印机:来自 main 的你好 测试打印机:来自线程 11 的你好 测试打印机:来自线程 10 的你好

【讨论】:

  • 很遗憾,.NET Standard 不支持。此外,这些天我会犹豫将 System.Runtime.Remoting 命名空间中的任何内容添加到任何新项目中。
  • @NightOwl888,它们已被较新的框架公开为AsyncLocal。我认为这个功能不会很快消失(考虑到无数的框架特性依赖于它)。答案已更新。
猜你喜欢
  • 2016-03-02
  • 1970-01-01
  • 2021-01-20
  • 1970-01-01
  • 1970-01-01
  • 2011-06-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
相关资源
最近更新 更多