Здравствуйте, -Cheese-, Вы писали:
C>Всем привет. C>Сталкивался ли кто-нибудь с проблемой в стандартном asp.net приложении, когда внутри асинхронного метода контроллера при асинхронном вычитывании тела запроса поток внезапно прерывается без исключения.
C>Т.е. есть что-то типа C>[cs] C>.... C>public class MyController : ControllerBase { C>.... C> [HttpPost] C> [Route("somepost")] C> public async Task<string> PostRequest() { C> HttpContextLocal context = new HttpContextLocal(HttpContext); C> context.SetBody(); C> log.Info($"Body length after read: {context.body?.Length}"); C> return await Handler.Application_POST(context); C> }
C>.... C>} C>....
a await context.SetBody(); не надо поставить?
и солнце б утром не вставало, когда бы не было меня
Немного утрировано:
* PostRequest получит управление и начнет выполнять свой код
* Дойдет до context.SetBody();
* Внутри context.SetBody(); дойдет до readResult = await bodyReader.ReadAsync();
* Тут начнется асихронное выполнение ReadAsync, поскольку await выше нет, то управление вернется PostRequest
* PostRequest продолжит свою работу и если "фоном" SetBody не успел сделать свою работу, то значит результата и не будет.
Лечить как уже сказали: await context.SetBody(); + public async Task SetBody()
И запсать себе что "async void" в 99.9% это антипаттерн и делать так можно если на 100% уверен и понимаешь что происходит.
В оставшемся 0.1% это реализация fire-and-forget, когда мы что-то запустили в работу но нам без разница когда это закончится и успешно ли вообще это закончится или упадет.
public async Task<string> PostRequest() {
.....
await context.SetBody();
.....
public class HttpContextLocal{
.....
public async voidTask SetBody() {
.....
Думаю тут так надо. ConfigureAwait(false) вроде по умолчанию для asp.net. можно экспериментально проверить.
Здравствуйте, -Cheese-, Вы писали:
T>>ещё ты мог бы возвращать не void a Task в SetBody и в контролере ожидать с await C>спасибо, это действительно можно попробовать
Не "можно", а "нужно". async void — как раз та самая сигнатура, что не сохраняет контекста исключения.
C> context.SetBody();
C> log.Info($"Body length after read: {context.body?.Length}");
C>
Собственно твой код вызывается в том же потоке, что и PostRequest. Скорее всего его тупо рубят на физическом уровне. Твой метод SetBody — это обычный синхронный метод. Модификатор async не делает метод автоматически асинхронным. Скорее всего у тебя об этом ворнинги в выхлоп компилятора пишут, а ты их игнорируешь. Тебе нужно внутри цикла или перед ним вызывать нечто с await (что создаст и запустит новый поток) или самому явно позвать await Task.Run(...).
Кроме того я бы посоветовал после каждого await-выражения дописывать .ConfigureAwait(false), чтобы случайно не захватывать контекст из которого был вызов (а осуществлять продолжение всегда на свободном потоке из пула).
Есть логика намерений и логика обстоятельств, последняя всегда сильнее.
Здравствуйте, Serginio1, Вы писали:
S> Это ты описал работу TaskCompletionSource
Нет. Это я описал общую логику async. Потоки ни от куда не берутся. Они всегда создаются явно. К самому механизму async это отношения не имеет. Автомат там почти точно такой же, как и в энумераторах с той лишь разницей, что вместо возврата последовательности тут получается последовательность вызовов. Вместо КА могли бы быть монады. Кстати, они сильно универсальнее.
ЗЫ
Пожалуйста, цитируй только то, что нужно для понимания твоего ответа. Не вынуждай банить.
Есть логика намерений и логика обстоятельств, последняя всегда сильнее.
Здравствуйте, Sharov, Вы писали:
S>Скажем так, async гарантия того, что rt будет пытаться запустить соотв. фрагмент кода в каком-нибудь потоке, т.е.
Это как раз главная ошибка все кто пытается использовать async. Правда в том, что async — это тупой конечный автомат, который всегда запускает асинхронный метод в том же потоке, что и содержит await. А вот что будет делать вызванный метод — это зависит от его реализации. Так же не ясно где будет продолжаться вызывающий метод, так как async тупо переключает лишь автомат, а поток на котором будет продолжение зависит от авэйтера и контекста.
Потому мне эти async-ки и не нравятся. Уж больно не понятно для людей что в реальности происходит. Лучше уж явно ими управлять. А еще лучше использовать подход акторов, когда все коммуникации между потоками происходят через очереди сообщений.
S>будет запущена машинерия выбора потока.
Что за невиданная зверушка эта твоя "машинерия выбора потока"?
S>Но это не наш случай. Никакого "рубят на физическом уровне" тут быть не должно.
Это почему? Я с вебом особо не работаю, но почему бы потоку не быть запущенным веб сервером написанному на плюсах?
В прочем, эти долбанные async и исключения могут потерять. Там много чего не продумано.
S>Ничего он его не блокирует, а возвращает в пул потоков.
Это кто тебе сказал? Там под капотом (я поглядел) Pipe.ReadAsync вызывается. Он может спокойно блокироваться. Вообще, это еще одно заблуждение, что эти долбанные ReadAsync реально всегда асинхронные. Попробуй читать/писать в разны потоках и поймешь, что это не так. А вот если явно стартануть новый поток (взять из пула), то асинхронность гарантирована.
S>Соглашусь -- мне они нелегко даются + куча возможностей себе отстрелить что-нибудь. С др. стороны НС S>когда неплохо объяснил, что это всего лишь навсего нарезка кода на всяческие promis'ы с поддержкой компилятора.
Ну, вот все это сделано не системно и слишком много неявного. Я ради развлечения реализовывал многопоточность разными методами и пришел к тому, что лучший вариант — это очереди сообщений и модель акторов (когда экземпляр класса разгребает свою очередь и отвечает путем посылки асинхронных сообщений в другие очереди. При этом ты просто забываешь о потоках и мыслишь в терминах посылки сообщений. Там тоже есть вои нюансы, но ошибки выявлятся элементарно, в отличии от прямой работы с потоками и блокировками и тем более async-ами. Вот их мало кто до конца понимает. Вроде смотришь в код и он очевиден, но не фига это не так.
S>Выше я уже ответил, что понимаю под асинхронностью.
Ну, вот в том-то и проблема, что ты тоже до конца эту модель не понимаешь. А лично я и не хочу ее понимать. Время можно проводить интереснее.
Есть логика намерений и логика обстоятельств, последняя всегда сильнее.
Всем привет.
Сталкивался ли кто-нибудь с проблемой в стандартном asp.net приложении, когда внутри асинхронного метода контроллера при асинхронном вычитывании тела запроса поток внезапно прерывается без исключения.
Т.е. есть что-то типа
....
public class MyController : ControllerBase {
....
[HttpPost]
[Route("somepost")]
public async Task<string> PostRequest() {
HttpContextLocal context = new HttpContextLocal(HttpContext);
context.SetBody();
log.Info($"Body length after read: {context.body?.Length}");
return await Handler.Application_POST(context);
}
....
}
....
public class HttpContextLocal{
.....
public async void SetBody() {
try {
log.Info($"Body => Position: {context.Request.Body.Position}, CanRead: {context.Request.Body.CanRead}");
var bodyReader = context.Request.BodyReader;
ReadResult readResult;
int cnt=0;
while (true) {
readResult = await bodyReader.ReadAsync();
log.Info($"Read step {++cnt} => IsCompleted: {readResult.IsCompleted}, IsCanceled: {readResult.IsCanceled}");
if (readResult.IsCompleted || readResult.IsCanceled)
break;
log.Info($"Buffer length: {readResult.Buffer.Length}");
bodyReader.AdvanceTo(readResult.Buffer.Start, readResult.Buffer.End);
}
body = Encoding.UTF8.GetString(readResult.Buffer.ToArray());
log.Info($"Body read ok => length: {body?.Length}");
....
} catch (Exception ex) {
log.Error(ex);
......
}
}
}
В некоторых ситуациях, прибольшом теле запроса и интенсивной нагрузке на хост вижу в логе примерно вот такую картину...
в логе нет и переменная context.body в методе контроллера пустая, исключния так же не было — такое впечатление, что внутри цикла метода SetBody просто поток прекратил существование
так проставь в самом конролере try catch для того, чтобы наверняка отловить исключение, тогда и видно будет, в чём проблема,
кроме того, не видно, что происходит в конструкторе, может, проблема там?
ещё ты мог бы возвращать не void a Task в SetBody и в контролере ожидать с await
Здравствуйте, takTak, Вы писали:
T>так проставь в самом конролере try catch для того, чтобы наверняка отловить исключение, тогда и видно будет, в чём проблема, T>кроме того, не видно, что происходит в конструкторе, может, проблема там?
ну оно ж не вывалилось....
T>ещё ты мог бы возвращать не void a Task в SetBody и в контролере ожидать с await
спасибо, это действительно можно попробовать
Здравствуйте, -Cheese-, Вы писали:
C>Сталкивался ли кто-нибудь с проблемой в стандартном asp.net приложении, когда внутри асинхронного метода контроллера при асинхронном вычитывании тела запроса поток внезапно прерывается без исключения.
Есть не перехватываемые исключения вроде StackOverflowException и некоторые виды AV при повреждении памяти. Ты как понимаешь, что исключений не было? Под отладчиком пускал?
Есть логика намерений и логика обстоятельств, последняя всегда сильнее.
Здравствуйте, -Cheese-, Вы писали:
C>Всем привет. C>Сталкивался ли кто-нибудь с проблемой в стандартном asp.net приложении, когда внутри асинхронного метода контроллера при асинхронном вычитывании тела запроса поток внезапно прерывается без исключения.
C>Т.е. есть что-то типа C>
C>....
C>public class MyController : ControllerBase {
C>....
C> [HttpPost]
C> [Route("somepost")]
C> public async Task<string> PostRequest() {
C> HttpContextLocal context = new HttpContextLocal(HttpContext);
C> context.SetBody();
C> }
C>....
C>}
C>....
C>public class HttpContextLocal{
C> public async void SetBody() {}
C>
Попробуй добавить await перед context.SetBody() и возвращаемый тип Task вместо void для SetBody()
Здравствуйте, -Cheese-, Вы писали:
C>в логе нет и переменная context.body в методе контроллера пустая, исключния так же не было — такое впечатление, что внутри цикла метода SetBody просто поток прекратил существование
Здравствуйте, VladD2, Вы писали:
VD>Собственно твой код вызывается в том же потоке, что и PostRequest.
Это почему? Из-за void (async void)?
VD>Скорее всего его тупо рубят на физическом уровне.
Что значит "рубят на физическом уровне"?
VD> Тебе нужно внутри цикла или перед ним вызывать нечто с await (что создаст и запустит новый поток) или самому явно позвать await Task.Run(...).
Так есть же в цикле readResult = await bodyReader.ReadAsync();
Здравствуйте, Sharov, Вы писали:
S>Это почему? Из-за void (async void)?
Бай дизайн. Ключевое свойство async ни хрена не приводит к асинхронности (переводу выполнения на другой поток). Запуск потока где–то должен случиться явно. Даже await–вызовы начинаются синхронно и уже внутри тем или иным методом инициируют асинхронность.
S>Что значит "рубят на физическом уровне"?
Когда поток срубается дотнетом, в нем эмулируется исключение ThreadAbortException. Но есши потгк создан и контролируется анменеджед–кодом он может быть срублен вызовом TerminateThread. При этом эмулировать генерацию ThreadAbortException будет некому и отряд просто не заметит потери бойца.
S>Так есть же в цикле readResult = await bodyReader.ReadAsync();
Откровенно говоря не заметил await в первый раз. Подсветка подвела.
Очевидно он начинается на исходном потоке или блокирует его. Плюс там возможен захват контекста и попытка продолжиться на нем же (предположение).
Все эти async–и в дотнете сделаны кривовато. Слишком много не явного (не явные глобальные переменные контекста, не внятная семантика асинхронности).
Люди свято верят, если метод async, то код уже в другом потоке. Но это не так. Где–то асинхронность должна инициироватся явно. А то, что метод с async это делает, не более чем соглашение. Это надо держать в уме.
Есть логика намерений и логика обстоятельств, последняя всегда сильнее.
VD>Люди свято верят, если метод async, то код уже в другом потоке. Но это не так. Где–то асинхронность должна инициироватся явно. А то, что метод с async это делает, не более чем соглашение. Это надо держать в уме.
MoveNext()
Перемещает конечный автомат в его следующее состояние.
SetStateMachine(IAsyncStateMachine)
Настраивает конечный автомат с размещенной в куче репликой.
и солнце б утром не вставало, когда бы не было меня
Это вообще не о том. Эта статья о том как режется метод на части с продолжениями. Но никто не гарантирует, что вызов с async прямо перемещается на другой поток.
Вот этот Task.Delay(1000) из статьи, например, создает внутри себя System.Threading.Timer с интервалом 1000 миллисекунд и задает ему делегат, который вызывает Task.Complete(). Вот от этого таймера и появляется асинхронность, так как события таймера вызываются на потоках из пула. А если бы Task.Delay() тупо закомплитил задачу прямо в своем теле, то никаких потоков не создалось бы.
Есть логика намерений и логика обстоятельств, последняя всегда сильнее.
Здравствуйте, VladD2, Вы писали:
VD>Вот этот Task.Delay(1000) из статьи, например, создает внутри себя System.Threading.Timer с интервалом 1000 миллисекунд и задает ему делегат, который вызывает Task.Complete(). Вот от этого таймера и появляется асинхронность, так как события таймера вызываются на потоках из пула. А если бы Task.Delay() тупо закомплитил задачу прямо в своем теле, то никаких потоков не создалось бы.
Это ты описал работу TaskCompletionSource
В статье
AsyncTaskMethodBuilder(<>t__builder) — представляет построитель для асинхронных методов, которые возвращают задачу. Этот вспомогательный тип и его члены предназначены для использования компилятором. Здесь инкапсулирована логика, общая для всех конечных автоматов. Именно этот тип создает объект Task, возвращаемых заглушкой. На самом деле этот тип очень похож на класс TaskCompletionSource в том смысле, что он создает задачу-марионетку, которую можно сделать завершенной позже. Отличие от TaskCompletionSource заключается в том, что AsyncTaskMethodBuilder оптимизирован для async-методов и ради повышения производительности является структурой, а не классом.
TaskAwaiter(<>u_awaiter) — здесь хранится временный объект, который ожидает завершения асинхронной задачи. Также представлен в виде структуры и помогает оператору await подписаться на уведомление о завершении задачи Task.
Суть в
В процедуре подписки на уведомление участвует также объект AwaitUnsafeOnCompleted. Именно здесь реализуются дополнительные возможности await, в том числе запоминание контекста синхронизации, который нужно будет восстановить при возобновлении. Этот метод планирует конечный автомат для перехода к следующему действию по завершении выполнения указанного объекта типа awaiter. В качестве параметров: AsyncTaskMethodBuilder.AwaitOnCompleted(ref TAwaiter awaiter, ref TStateMachine stateMachine). Как видно перед вызовом этого метода происходит загрузка в стек двух переменных с индексом 2 и 3, где 2 — valuetype [mscorlib]System.Runtime.CompilerServices.TaskAwaiter, 3 — class Asynchronous.Program/'d__0'
Рассмотрим немного подробнее структуру AsyncTaskMethodBuilder(сильно глубоко копать здесь не буду, потому что на мой взгляд изучение этой структуры и все, что с ней связано можно расписать как бы не на несколько статей):
/// <summary>Кэшированная задача для default(TResult).</summary>
internal readonly static Task<TResult> s_defaultResultTask = AsyncTaskCache.CreateCacheableTask(default(TResult));
/// <summary>Состояние, связанное с IAsyncStateMachine.</summary>private AsyncMethodBuilderCore m_coreState; // mutable struct: must not be readonly
/// <summary>Ленивая инициализация задачи</summary>private Task<TResult> m_task; // lazily-initialized: must not be readonly
/// <summary>
/// Планирует состояние данной машины для дальнейшего действия, когда awaiter выполнится /// </summary>
/// <typeparam name="TAwaiter">Определяет тип awaiter.</typeparam>
/// <typeparam name="TStateMachine">Определяет тип состояния машины.</typeparam>
/// <param name="awaiter">The awaiter.</param>
/// <param name="stateMachine">Состояние машины.</param>
[SecuritySafeCritical]
public void AwaitUnsafeOnCompleted<TAwaiter, TStateMachine>(
ref TAwaiter awaiter, ref TStateMachine stateMachine)
where TAwaiter : ICriticalNotifyCompletion
where TStateMachine : IAsyncStateMachine
{
try
{
AsyncMethodBuilderCore.MoveNextRunner runnerToInitialize = null;
var continuation = m_coreState.GetCompletionAction(AsyncCausalityTracer.LoggingOn ? this.Task : null, ref runnerToInitialize);
// Если это первый await, то мы не упаковали состояние машины и должны сделать это сейчасif (m_coreState.m_stateMachine == null)
{
// Действие задачи должно быть проинициализировано до первого приостановления var builtTask = this.Task;
//Упаковка состояния машины при помощи вызова internal-метода,
// где ссылка будет храниться в кеше.
m_coreState.PostBoxInitialization(stateMachine, runnerToInitialize, builtTask);
}
awaiter.UnsafeOnCompleted(continuation);
}
catch (Exception e)
{
AsyncMethodBuilderCore.ThrowAsync(e, targetContext: null);
}
}
Рассмотрим вкратце, что внутри этой структуры:
s_defaultResultTask = AsyncTaskCache.CreateCacheableTask(default(TResult)) — здесь создается задача без реализации Dispose при указании специального флага DoNotDispose. Данный подход используется при создании задач для кеширования или повторного использования.
AsyncMethodBuilderCore m_coreState — представляет состояние, связанное с выполнением IAsyncStateMachine. Это структура.
AsyncMethodBuilderCore.MoveNextRunner runnerToInitialize — предоставляет возможность вызова метода конечного автомата MoveNext согласно предоставленному контексту выполнения программы. Это структура, которая содержит контекст выполнения, состояние конечного автомата и метод для выполнения MoveNext.
m_coreState.GetCompletionAction(AsyncCausalityTracer.LoggingOn? this.Task: null, ref runnerToInitialize) — получает Action при ожидании метода UnsafeOnCompleted и происходит прикрепление как задачи продолжения. Здесь также происходит запоминание состояния машины и контекста выполнения.
awaiter.UnsafeOnCompleted(continuation) — планирует продолжение действий, которые будут вызваны, когда экземпляр завершит выполнение. При этом в зависимости от того, нужно ли нам восстанавливать контекст или нет, будет вызван метод MoveNext соответственно с контекстом да приостановки метода или же выполнение продолжится в контексте того потока, в котором происходило выполнение задачи.
и солнце б утром не вставало, когда бы не было меня
Здравствуйте, VladD2, Вы писали:
VD>Нет. Это я описал общую логику async. Потоки ни от куда не берутся. Они всегда создаются явно. К самому механизму async это отношения не имеет. Автомат там почти точно такой же, как и в энумераторах с той лишь разницей, что вместо возврата последовательности тут получается последовательность вызовов. Вместо КА могли бы быть монады. Кстати, они сильно универсальнее.
Я все к тому, что вся кухня async это довольно сложный механизм. Там же весь async может происходить в одном потоке например при вызове Task.FromResult или
или тот же TaskCompletionSource создан без RunContinuationsAsynchronously то продолжение будет в том же потоке, что и вызван SetResult.
Тут не надо верить, нужно разбираться в тонкостях.
Ну и опять же, потоки явно не создаются а берутся уже из пула потоков или выполняются в потоке синхронизации или в том же потоке.
Я к тому, что нужно давать более точный ответ. Это полезно для задающего вопрос.
и солнце б утром не вставало, когда бы не было меня
Здравствуйте, VladD2, Вы писали:
VD>Когда поток срубается дотнетом, в нем эмулируется исключение ThreadAbortException.
В Core оно так не работает.
VD>Откровенно говоря не заметил await в первый раз. Подсветка подвела.
В старом ASP.NET, который в FW, контекст синхронизации не создает потоки, весь асинхронный код в рамках одного запроса выполняется в одном потоке. Причем, в отличие от винформсов, там еще и message loop с пробросом коллбеков через него отсутствует.
В ASP.NET Core контекст синхронизации дефолтный, т.е. использует для асинхронных операций потоки из пула.
VD>Люди свято верят, если метод async, то код уже в другом потоке. Но это не так. Где–то асинхронность должна инициироватся явно.
Не обязательно. Все зависит от контекста синхронизации.
Здравствуйте, Ночной Смотрящий, Вы писали:
НС>В Core оно так не работает.
С коркой почти не работал. А как же там? Нет ThreadAbortException?
НС>В старом ASP.NET, который в FW, контекст синхронизации не создает потоки, весь асинхронный код в рамках одного запроса выполняется в одном потоке. Причем, в отличие от винформсов, там еще и message loop с пробросом коллбеков через него отсутствует.
Потоки должны создавать те методы, что таски возвращают. Скажем если вызвать Delay, то он создаст таймер, а тот сработав создаст продолжение на потоке из пула.
НС>В ASP.NET Core контекст синхронизации дефолтный, т.е. использует для асинхронных операций потоки из пула.
Контекст работает при продолжении. А вот сам метод должен как-то асинхронность реализовать. Delay использует таймер и потоки из пула. Yield в зависимости от условий ставит задачу в очередь или что-то там делает. А в его случае используется ReadAsync из PipeReader-а, который в итоге ReadAsync на пайпе вызывает. Поток срубают когда вызов в его кишках находится.
Собственно ему уже помогло явный запуск задачи.
Есть логика намерений и логика обстоятельств, последняя всегда сильнее.
S>>Это почему? Из-за void (async void)? VD>Бай дизайн. Ключевое свойство async ни хрена не приводит к асинхронности (переводу выполнения на другой поток). Запуск потока где–то должен случиться явно. Даже await–вызовы начинаются синхронно и уже внутри тем или иным методом инициируют асинхронность.
Скажем так, async гарантия того, что rt будет пытаться запустить соотв. фрагмент кода в каком-нибудь потоке, т.е.
будет запущена машинерия выбора потока. Что не отменяет того факта, что фрагмент кода будет запущен в том же потоке,
т.е. синхронно. Под асинхронностью я называю "запуск выбора потока для задачи". И в данном случае задача вполне аснхрнна.
S>>Что значит "рубят на физическом уровне"?
VD>Когда поток срубается дотнетом, в нем эмулируется исключение ThreadAbortException. Но есши потгк создан и контролируется анменеджед–кодом он может быть срублен вызовом TerminateThread. При этом эмулировать генерацию ThreadAbortException будет некому и отряд просто не заметит потери бойца.
Но это не наш случай. Никакого "рубят на физическом уровне" тут быть не должно.
S>>Так есть же в цикле readResult = await bodyReader.ReadAsync(); VD>Откровенно говоря не заметил await в первый раз. Подсветка подвела. VD>Очевидно он начинается на исходном потоке или блокирует его. Плюс там возможен захват контекста и попытка продолжиться на нем же (предположение).
Ничего он его не блокирует, а возвращает в пул потоков.
VD>Все эти async–и в дотнете сделаны кривовато. Слишком много не явного (не явные глобальные переменные контекста, не внятная семантика асинхронности).
Соглашусь -- мне они нелегко даются + куча возможностей себе отстрелить что-нибудь. С др. стороны НС
когда неплохо объяснил, что это всего лишь навсего нарезка кода на всяческие promis'ы с поддержкой компилятора.
VD>Люди свято верят, если метод async, то код уже в другом потоке. Но это не так. Где–то асинхронность должна инициироватся явно. А то, что метод с async это делает, не более чем соглашение. Это надо держать в уме.
Выше я уже ответил, что понимаю под асинхронностью.
Здравствуйте, VladD2, Вы писали:
VD>Здравствуйте, Sharov, Вы писали:
S>>Скажем так, async гарантия того, что rt будет пытаться запустить соотв. фрагмент кода в каком-нибудь потоке, т.е. VD>Это как раз главная ошибка все кто пытается использовать async. Правда в том, что async — это тупой конечный автомат, который всегда запускает асинхронный метод в том же потоке, что и содержит await. А вот что будет делать вызванный метод — это зависит от его реализации. Так же не ясно где будет продолжаться вызывающий метод, так как async тупо переключает лишь автомат, а поток на котором будет продолжение зависит от авэйтера и контекста.
Дал маху, проверил -- компилятор предупреждает, что async без await будет синхронным. Думал, что метод
вообще не скомпилируется. Короче, без await "машинерия выбора потока" не будет работать. Собственно, это
и есть нарезка на промисы.
VD>Потому мне эти async-ки и не нравятся. Уж больно не понятно для людей что в реальности происходит. Лучше уж явно ими управлять. А еще лучше использовать подход акторов, когда все коммуникации между потоками происходят через очереди сообщений.
Как и везде, нужно время чтобы привыкнуть и приноровиться.
S>>будет запущена машинерия выбора потока. VD>Что за невиданная зверушка эта твоя "машинерия выбора потока"?
КА.
S>>Но это не наш случай. Никакого "рубят на физическом уровне" тут быть не должно. VD>Это почему? Я с вебом особо не работаю, но почему бы потоку не быть запущенным веб сервером написанному на плюсах?
Ну потому что это будет работать под IIS, который, вроде сам unmanaged, но будет хостить managed host.
Следовательно, все потоки будут managed.
VD>В прочем, эти долбанные async и исключения могут потерять. Там много чего не продумано.
Не могут, кроме случаев с async void (выше писали).
S>>Ничего он его не блокирует, а возвращает в пул потоков. VD>Это кто тебе сказал? Там под капотом (я поглядел) Pipe.ReadAsync вызывается. Он может спокойно блокироваться. Вообще, это еще одно заблуждение, что эти долбанные ReadAsync реально всегда асинхронные. Попробуй читать/писать в разны потоках и поймешь, что это не так. А вот если явно стартануть новый поток (взять из пула), то асинхронность гарантирована.
Вызывающий поток блокируется? Где на код можно глянуть?
VD>Ну, вот в том-то и проблема, что ты тоже до конца эту модель не понимаешь. А лично я и не хочу ее понимать. Время можно проводить интереснее.
Сейчас без этого понимания работу с улицы едва найдешь.
Совсем не обязательно. Continuation для любого таска дефолтный скедулер вызывает через контекст синхронизации. Дефолтный контекст при этом сам порождает потоки, винформсный прокидывает вызов через message loop, а вот старый аспнет выполняет в том же потоке, поэтому если там какая то блокировка — привет, дедлок.
А еще есть Task.Run, который сам берет потоки из пула, вне зависимости от контекста.