ОБНОВЛЕНИЕ:
Детали того, что происходит, важны, но я начну с простого ответа. Ваш главный вопрос: как вы решаете проблему с получением вашей локальной информации о состоянии потока? Если вы написали свои методы Begin и EndInvoke как методы в коде для страницы, у вас есть доступ к любому члену данных на странице. Поэтому просто создайте личный элемент данных IPrincipal principal_
и CultureInfo culture_
членов данных в своем коде и инициализируйте их в вашем методе page_load. Тогда из ваших методов Begin и EndInvoke у вас будет прямой доступ к ним.
Теперь дьяволы в деталях, которые, мы надеемся, ответят на ваши вопросы «в чем суть»:
Сначала я расскажу о пулах потоков, потому что не думаю, что вам понятно, как потоки относятся к ним. ThreadPool может иметь 1 поток или N рабочих потоков, работающих над ним. Когда делегат добавляется в пул потоков, он обычно не создает новые потоки. Что произойдет, поток, назначенный пулу, который уже ничего не делает, придет и выполнит некоторую работу ... если поток завершает работу до переключения контекста, он может выполнить другую задачу и запустить ее. Один поток в пуле потоков не ограничен одной задачей. Поток может выполнить несколько задач, которые могут вызывать несколько асинхронных обработчиков последовательно. Тот факт, что поток из пула потоков может вызывать асинхронный обработчик, не имеет значения. Таким образом, многие делегаты могут быть запущены с относительно небольшим количеством потоков и меньшим количеством переключателей контекста. Когда пул потоков используется в асинхронном шаблоне большую часть времени, детали заданий запрашивают, завершена ли операция блокировки, и, если она была, вызывает следующий обратный вызов, чтобы продолжить или завершить работу асинхронной операции. У вас могут быть тысячи обработчиков, блокирующих и опрашивающих, и только несколько потоков когда-либо занимаются их обработкой, потому что в большинстве случаев только немногие из них должны выполнить свою работу. Это эффективно, и в этом все дело!
Важно отметить, что вызов метода Async Handler BeginInvoke / EndInvoke НЕ означает, что он использует поток или использует ThreadPool. Если IO (или какая-либо другая задача) достаточно быстрая, во многих случаях вообще ничего не помещается в пул потоков. Реализация имеет возможность НИКОГДА не создавать поток или помещать задание в пул потоков. Если он этого не делает, то асинхронный обработчик никогда не переключит контекст и вообще не будет работать параллельно с вызывающим потоком. Это нормально. Асинхронные методы требуются только для того, чтобы выполнять как можно больше работы, и только для использования пула потоков, если ему нужно что-то ждать. Асинхронный метод и обратные вызовы никогда не могут быть помещены в отдельный поток. Это важное различие, которое делает ситуации, такие как буферизованные асинхронные вызовы ввода-вывода, эффективными, не требуя переключения контекста.
Книга C # 4.0 в двух словах - это ОТЛИЧНЫЙ ресурс, который очень хорошо объясняет шаблон и методологию Async.
Уточнение, которое вы делаете с помощью ссылки MSDN , вводит в заблуждение, поскольку вы не указали полный контекст:
Примеры кода в этом разделе демонстрируют четыре распространенных способа использования
BeginInvoke и EndInvoke для выполнения асинхронных вызовов. После звонка
BeginInvoke вы можете сделать следующее:
Сделайте некоторую работу и затем вызовите EndInvoke для блокировки до вызова
завершается.
Получить WaitHandle с помощью System.IAsyncResult.AsyncWaitHandle
свой метод WaitOne, чтобы заблокировать выполнение до
Сигнал WaitHandle, а затем вызвать EndInvoke.
Опрос IAsyncResult, возвращенный BeginInvoke, чтобы определить, когда
Асинхронный вызов завершен, а затем вызовите EndInvoke.
Передать делегат для метода обратного вызова в BeginInvoke. Метод
выполняется в потоке ThreadPool после завершения асинхронного вызова.
Метод обратного вызова вызывает EndInvoke.
Как вы можете видеть, указанная вами точка маркера относится к примеру и одному варианту из нескольких, поэтому НЕ гарантируется, что какой-либо метод выполняется в ThreadPool. Также ваша ссылка на меня в комментариях объясняет ситуацию для AddOnPreRenderCompleteAsync, которая вызывает асинхронный обработчик в пуле потоков, но, опять же, это все еще ситуативно для их примера. ( Статья )
Однако важно то, что вы сказали, что дважды проверили, что ваш метод (какой?) Вызывается в другом потоке. Вероятно, это ваш метод обратного вызова, который вы зарегистрировали в другой асинхронной операции? Это может быть выполнено в другом потоке, если вы вызовете BeginInvoke для другого асинхронного метода из ваших собственных обработчиков. Прежде чем делать какие-либо подобные вызовы, вы, скорее всего, все еще находитесь в теме, связанной со страницей. Даже если вы сделали вызов асинхронного метода, который реализует сам себя, помещая что-то в пул потоков в какой-то момент, если эта операция была достаточно быстрой, возможно, он может на самом деле пропустить этот шаг! Единственная причина, по которой я так долго объяснил один и тот же результат окончания в другом потоке по другой причине, заключается в том, что вы понимаете, что поведение асинхронного вызова НЕ требует пула потоков, но вы не можете полагаться на контекст переключаться или нет, и точка асинхронных вызовов - не параллелизм, а эффективность.