ThreadStatic for TPL Task

一个人想着一个人 提交于 2020-01-24 02:08:05

问题


How can something like a ThreadStatic be used in a TPL Task? My understanding ("Wrox Professional Parallel Programming with C#", p74) is that a Task can switch from one thread to another during execution.

What I want to do?

I want to maintain a session id inside a static class so I don't need to pass this id to all of my methods. My library has methods like login(id), logout(id) and many methods which operate on credentials associated with this id. But I don't want to pass this id to every method. I can make sure my library is called within different thread for different sessions. So saving the id inside login() in a ThreadStatic variable will work.

Now I want to use TPL Tasks which are created for me by a ThreadPool. I can pass my session id to the Task, but if I store this id inside a ThreadStatic variable it will not survive if my Task switches threads.


回答1:


TPL and .Net 4.5's async flow the ExecutionContext, which means you can use CallContext.LogicalSetData(string, object) and CallContext.GetLogicalData(string) in much the same way you would use ThreadStatic. It does incur a significant performance penalty, however. This has been exposed in .Net 4.6 and up (including .Net Standard 1.3 and up) with the AsyncLocal<> wrapper.

See Async Causality Chain Tracking, How to include own data in ExecutionContext, and ExecutionContext vs SynchronizationContext for a deeper dive.

Example use:

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;
        }
    }
}

Prints:

Test Printer: hello from main  
Test Printer: hello from thread 11 
Test Printer: hello from thread 10



回答2:


I'd say avoid the thread static unless you've some guarantee about which tasks run on which threads. Just pass it. Your intent will be clearer.




回答3:


You are correct, thread-static is not suitable for Tasks.

Better overcome your problems with passing the parameter. It is a lot clearer and cleaner to just pass it. A little bit of typing will get you lots more readability and thread-safety.



来源:https://stackoverflow.com/questions/6896652/threadstatic-for-tpl-task

易学教程内所有资源均来自网络或用户发布的内容,如有违反法律规定的内容欢迎反馈
该文章没有解决你所遇到的问题?点击提问,说说你的问题,让更多的人一起探讨吧!