Сообщение Re[6]: Объясните поведение ContinueWith(..., TaskContinuatio от 02.01.2017 21:56
Изменено 02.01.2017 21:58 AK107
Re[6]: Объясните поведение ContinueWith(..., TaskContinuationOptions.ExecuteSync
Здравствуйте, Sinix, Вы писали:
S>Вообще, закладываться на такое поведение опасно, т.к. это просто побочный эффект реализации и никто не запрещает .WhenAll добавляться в начало списка продолжений. Лично я бы переписал код на await-ах или сохранял бы в список запущенные задачи. Все прочие варианты (включая AttachedToParent) ненадёжны.
оценку я не заслужил
вопрос о порядке выполнения возник давеча когда делал custom target для NLog. там такой изврат на самописных continuations для асинхронного логирования да еще и не задокументированный нормально. особый головняк который нужно реализовывать — это FlushAsync, где, как оказалось, нужно дождаться реального завершения всех активных тасков. можно сделать и на await, но придется городить обработку ошибок, тогда как здесь ничего лишнего. вот такое получилось:
в принципе требования удаления из pendingEvents до фактического завершения FlushAsync (через ContinueWith) не критично, но хотелось гарантий на будущее расширение функционала. добавлять же сам ContinueWith в pendingEvents некошерно, т.к. в этом случае, теоретически, TryRemove может отработать до TryAdd.
вопрос: разве ExecuteSynchronously в данном решении не дает гарантий, что первый COntinueWith выполнится до второго?
S>P.S. исходники удобнее смотреть на https://referencesource.microsoft.com/ или https://source.dot.net/ .
спасибо!
S>Вообще, закладываться на такое поведение опасно, т.к. это просто побочный эффект реализации и никто не запрещает .WhenAll добавляться в начало списка продолжений. Лично я бы переписал код на await-ах или сохранял бы в список запущенные задачи. Все прочие варианты (включая AttachedToParent) ненадёжны.
оценку я не заслужил
вопрос о порядке выполнения возник давеча когда делал custom target для NLog. там такой изврат на самописных continuations для асинхронного логирования да еще и не задокументированный нормально. особый головняк который нужно реализовывать — это FlushAsync, где, как оказалось, нужно дождаться реального завершения всех активных тасков. можно сделать и на await, но придется городить обработку ошибок, тогда как здесь ничего лишнего. вот такое получилось:
private readonly ConcurrentDictionary<AsyncLogEventInfo, Task> pendingEvents = new ConcurrentDictionary<AsyncLogEventInfo, Task>();
protected sealed override void Write(AsyncLogEventInfo info)
{
var task = WriteAsync(info);
pendingEvents.TryAdd(info, task);
task.ContinueWith(x =>
{
Task tmp;
pendingEvents.TryRemove(info, out tmp);
},
TaskContinuationOptions.ExecuteSynchronously);
}
private async Task WriteAsync(AsyncLogEventInfo info)
{
try
{
var result = await Client.PostAsync(...);
result.EnsureSuccessStatusCode();
info.Continuation(null);
}
catch (Exception ex)
{
info.Continuation(ex);
}
}
protected override void FlushAsync(AsyncContinuation asyncContinuation)
{
Task.WhenAll(pendingEvents.Values)
.ContinueWith(x => asyncContinuation(x.Exception), TaskContinuationOptions.ExecuteSynchronously);
}
в принципе требования удаления из pendingEvents до фактического завершения FlushAsync (через ContinueWith) не критично, но хотелось гарантий на будущее расширение функционала. добавлять же сам ContinueWith в pendingEvents некошерно, т.к. в этом случае, теоретически, TryRemove может отработать до TryAdd.
вопрос: разве ExecuteSynchronously в данном решении не дает гарантий, что первый COntinueWith выполнится до второго?
S>P.S. исходники удобнее смотреть на https://referencesource.microsoft.com/ или https://source.dot.net/ .
спасибо!
Re[6]: Объясните поведение ContinueWith(..., TaskContinuatio
Здравствуйте, Sinix, Вы писали:
S>Вообще, закладываться на такое поведение опасно, т.к. это просто побочный эффект реализации и никто не запрещает .WhenAll добавляться в начало списка продолжений. Лично я бы переписал код на await-ах или сохранял бы в список запущенные задачи. Все прочие варианты (включая AttachedToParent) ненадёжны.
оценку я не заслужил
вопрос о порядке выполнения возник давеча когда делал custom target для NLog. там такой изврат на самописных continuations для асинхронного логирования да еще и не задокументированный нормально. особый головняк который нужно реализовывать — это FlushAsync, где, как оказалось, нужно дождаться реального завершения всех активных тасков. можно сделать и на await, но придется городить обработку ошибок, тогда как здесь ничего лишнего. вот такое получилось:
в принципе требования удаления из pendingEvents до фактического завершения FlushAsync (через ContinueWith) не критично, но хотелось гарантий на будущее расширение функционала. добавлять же сам ContinueWith в pendingEvents или переносить в тело WriteAsync некошерно, т.к. в этом случае, теоретически, TryRemove может отработать до TryAdd.
вопрос: разве ExecuteSynchronously в данном решении не дает гарантий, что первый COntinueWith выполнится до второго?
S>P.S. исходники удобнее смотреть на https://referencesource.microsoft.com/ или https://source.dot.net/ .
спасибо!
S>Вообще, закладываться на такое поведение опасно, т.к. это просто побочный эффект реализации и никто не запрещает .WhenAll добавляться в начало списка продолжений. Лично я бы переписал код на await-ах или сохранял бы в список запущенные задачи. Все прочие варианты (включая AttachedToParent) ненадёжны.
оценку я не заслужил
вопрос о порядке выполнения возник давеча когда делал custom target для NLog. там такой изврат на самописных continuations для асинхронного логирования да еще и не задокументированный нормально. особый головняк который нужно реализовывать — это FlushAsync, где, как оказалось, нужно дождаться реального завершения всех активных тасков. можно сделать и на await, но придется городить обработку ошибок, тогда как здесь ничего лишнего. вот такое получилось:
private readonly ConcurrentDictionary<AsyncLogEventInfo, Task> pendingEvents = new ConcurrentDictionary<AsyncLogEventInfo, Task>();
protected sealed override void Write(AsyncLogEventInfo info)
{
var task = WriteAsync(info);
pendingEvents.TryAdd(info, task);
task.ContinueWith(x =>
{
Task tmp;
pendingEvents.TryRemove(info, out tmp);
},
TaskContinuationOptions.ExecuteSynchronously);
}
private async Task WriteAsync(AsyncLogEventInfo info)
{
try
{
var result = await Client.PostAsync(...);
result.EnsureSuccessStatusCode();
info.Continuation(null);
}
catch (Exception ex)
{
info.Continuation(ex);
}
}
protected override void FlushAsync(AsyncContinuation asyncContinuation)
{
Task.WhenAll(pendingEvents.Values)
.ContinueWith(x => asyncContinuation(x.Exception), TaskContinuationOptions.ExecuteSynchronously);
}
в принципе требования удаления из pendingEvents до фактического завершения FlushAsync (через ContinueWith) не критично, но хотелось гарантий на будущее расширение функционала. добавлять же сам ContinueWith в pendingEvents или переносить в тело WriteAsync некошерно, т.к. в этом случае, теоретически, TryRemove может отработать до TryAdd.
вопрос: разве ExecuteSynchronously в данном решении не дает гарантий, что первый COntinueWith выполнится до второго?
S>P.S. исходники удобнее смотреть на https://referencesource.microsoft.com/ или https://source.dot.net/ .
спасибо!