Сообщение Re[6]: async прерывание треда от 01.09.2021 17:45
Изменено 01.09.2021 20:15 VladD2
VD>Здравствуйте, Serginio1, Вы писали:
S>>Ну как здесь пишут https://habr.com/ru/post/260217/ возвращает он интерфейс IAsyncStateMachine с двумя методами
S>>https://docs.microsoft.com/ru-ru/dotnet/api/system.runtime.compilerservices.iasyncstatemachine?view=net-5.0
VD>Это вообще не о том. Эта статья о том как режется метод на части с продолжениями. Но никто не гарантирует, что вызов с async прямо перемещается на другой поток.
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 соответственно с контекстом да приостановки метода или же выполнение продолжится в контексте того потока, в котором происходило выполнение задачи.
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 соответственно с контекстом да приостановки метода или же выполнение продолжится в контексте того потока, в котором происходило выполнение задачи.