На каком потоке (-ах) WebClient поднимает свои события?

Я не могу найти документацию, которая указывает, какой поток WebClient вызывает события. Я провел несколько тестов и определил следующее:

  • Если вызван из потока пользовательского интерфейса (скажем, от обработчика события), обработчик события будет выполнен в этом потоке. В качестве теста я добавил бесконечный цикл после вызова OpenReadAsync. Обработчик событий никогда не вызывался.

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

Является ли это поведение документированным где угодно? Я ничего не нашел.

У меня есть в основном тот же вопрос, касающийся новых асинхронных функций С# - в итоге, асинхронный код должен быть выполнен. Будет ли это также появляться поток потока потока, когда нет потока пользовательского интерфейса? Будет ли это, в свою очередь, требовать потокобезопасный код?

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

Ответ 1

Для WebClient я также не нашел его документированным, но видел то же поведение, что и вы. По существу это можно охарактеризовать как "если есть активный контекст синхронизации при запуске вызова, он используется, иначе используется пул потоков".

Для асинхронного поведения на С# 5 это зависит от реализации того, что вы ожидаете... но я считаю, что awaiter для Task<T> будет использовать TaskScheduler.Current, чтобы запланировать продолжение - это означает, что вы увидите такое же поведение. (Это не обязательно только поток пользовательского интерфейса, который задает планировщик задач, но это самый очевидный пример.)

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

Если вы заинтересованы в том, как асинхронная зависания за кулисами, вы можете прочитать мою серию блога Eduasync.

Ответ 2

Класс WebClient реализует Асинхронный шаблон на основе событий. Шаблон полностью описан в Руководстве по дизайну рамок, но MSDN также содержит несколько подсказок, как это реализовано :

Реализаторы шаблона используют AsyncOperationManager для создания AsyncOperation для каждой асинхронной операции и повышения событий с помощью метода AsyncOperation.Post. Метод отправки выполняет пройденный обратный вызов на SynchronizationContext это current в то время, когда AsyncOperation.

По умолчанию SynchronizationContext в приложении WinForms или WPF используется поток пользовательского интерфейса или null в консольном приложении. WebClient Class, по-видимому, предпочитает поднимать события в потоке ThreadPool в последнем случае, но это детализация реализации.