Подтвердить что ты не робот

ThreadStatic для задачи TPL

Как может быть что-то вроде ThreadStatic в задаче TPL? Мое понимание ( "Wrox Professional Parallel Programming with С#", стр. 74) заключается в том, что Task может переключаться с одного потока на другой во время выполнения.

Что я хочу делать?

Я хочу поддерживать идентификатор сеанса внутри статического класса, поэтому мне не нужно передавать этот идентификатор ко всем моим методам. Моя библиотека имеет методы типа login(id), logout(id) и многие методы, которые работают с учетными данными, связанными с этим идентификатором. Но я не хочу передавать этот идентификатор каждому методу. Я могу убедиться, что моя библиотека вызывается в другом потоке для разных сеансов. Таким образом, сохранение id внутри login() в переменной ThreadStatic будет работать.

Теперь я хочу использовать TPL Tasks, созданные для меня ThreadPool. Я могу передать свой идентификатор сеанса в Задачу, но если я сохраню этот идентификатор внутри переменной ThreadStatic, он не сохранится, если моя задача переключит потоки.

4b9b3361

Ответ 1

TPL и .Net 4.5 выполняют асинхронную передачу ExecutionContext, что означает, что вы можете использовать CallContext.LogicalSetData(string, object) и CallContext.GetLogicalData(string) практически так же, как вы используете ThreadStatic. Однако это приводит к значительному снижению производительности. Это было раскрыто в .Net 4.6 и более поздних версиях (включая .Net Standard 1.3 и более поздних) с помощью оболочки AsyncLocal<>.

Подробнее о более глубоком погружении см. Асинхронное отслеживание причинно-следственных связей, Как включить собственные данные в ExecutionContext и ExecutionContext против 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;
        }
    }
}

Печать:

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

Ответ 2

Я бы сказал, чтобы избежать статического потока, если у вас нет гарантии, какие задачи выполняются на каких потоках. Просто передай это. Ваши намерения будут более ясными.

Ответ 3

Вы правы, thread-static не подходит для задач.

Лучше преодолеть свои проблемы с передачей параметра. Гораздо яснее и чище просто передать это. Немного набрав текст, вы получите большую удобочитаемость и безопасность потоков.