Сходство потоков с Async в контексте WinRT

Я хочу использовать существующий код с использованием ThreadStatic для реализации синглтона для каждого потока (например, Transaction.Current)

Однако это не работает с моделью async/await, работающей в новой задаче. Я знаю, что поскольку SynchronizationContext.Current имеет значение null, используется ThreadPoolTaskScheduler и продолжение может быть в другом потоке.

Например, если я выполню следующий код:

Task.Factory.StartNew(async () =>
{                    
   Debug.WriteLine("Start on thread {0}", Environment.CurrentManagedThreadId);
   await Task.Delay(10);
   Debug.WriteLine("Continue on thread {0}", Environment.CurrentManagedThreadId);
});

Результат:

Начать с 5 темы

Продолжить в теме 4

Моя реализация синглтона выглядит примерно так:

[ThreadStatic]
private static long? _sessionIndex; // Immutable value
private static ConcurrentDictionary<long, ...> contexts;
public static ... Current 
{
  get { 
    return contexts[_sessionIndex ?? (_sessionIndex=Interlocked.Increment(..))];
  }
}

Стивен Клири предлагает еще одно решение, основанное на ExecutionContext", который отлично работает в контексте 4.5.1, но не работает с ограниченной платформой winrt (CallContext не существует)

В качестве решения я бы попытался заставить выполнение продолжения в том же потоке, что и основная задача, чтобы сохранить механизм ThreadStatic.

Реализация TaskScheduler может быть решением, но вы не можете манипулировать потоком в winrt.

Возможно, с определенным SynchronizationContext, но я не нашел, как это сделать. Боюсь, это невозможно.

Есть идеи ?


person Alain Metge    schedule 12.01.2014    source источник


Ответы (1)


У меня есть пара типов AsyncContext и AsyncContextThread в моем Библиотека AsyncEx, определяющая однопоточный контекст. Они предоставляют пользовательские TaskScheduler и SynchronizationContext.

AsyncContextThread работает, беря задачу из пула потоков, устанавливая основной цикл и сохраняя эту задачу до завершения всех асинхронных операций. Планировщик задач/контекст синхронизации «возврат к тому же потоку» должен работать таким образом, потому что нет возможности поставить в очередь работу пула потоков для определенного потока.

person Stephen Cleary    schedule 12.01.2014
comment
Спасибо, Стивен. Вы спасли мне жизнь ;-) Еще один вопрос, нарушает ли ConfigureAwait(false) синхронизацию? - person Alain Metge; 14.01.2014
comment
@AlainMetge: Да; ConfigureAwait(false) говорит await не использовать SynchronizationContext (или TaskScheduler). - person Stephen Cleary; 14.01.2014
comment
Стивен, когда я пытаюсь использовать один из ваших модульных тестов с winrt, задача блокируется. Код такой: Async(async() =› { var context = new AsyncContextThread(); await context.Factory.Run(() =› 2); await context.JoinAsync(); }); с Async public static void Async(Func‹Task› test) { test().Wait(); } Как я могу выйти из задачи? - person Alain Metge; 17.01.2014
comment
(Stack Overflow — это сайт вопросов и ответов, а не форум. Вы разместили дополнительный вопрос в качестве ответа на свой предыдущий вопрос; в следующий раз задайте новый вопрос.) Вы столкнулись с классическая тупиковая ситуация, которую я описываю в своем блоге. Решение состоит в том, чтобы использовать модульный тест async вместо метода Async. - person Stephen Cleary; 17.01.2014