Собственные задачи - C#
Формулировка задачи:
Привет всем!
Столкнулся с тем, что в некоторых ситуациях Task.Run не сразу запускает задачу, а ставит на планирование. Первый вопрос - можно ли этого избежать? Пока ответа я не нашел, поэтому, а так же по ряду других ограничений, которые есть в оригинальной TaskModel, сделал свой пул потоков, работающий со своими задачами.
Сейчас делаю await (т.е. чтобы к моим задачам тоже можно было делать await). Создал awaiter, унаследовал интерфейс ICriticalNotifyCompletion, реализовал, всё как положено. Сделал сохранение контекста синхронизации, если таковой был.
Если я правильно понял, при срабатывании await у задачи вызывается GetAwaiter, который возвращает экземпляр этого самого Awaiter. У этого Awaiter вызывается метод OnCompleted, этому методу передается Continuation - делегат, оборачивающий весь код, который идет после await, и я должен его выполнить после того, как моя задача отработает. Я сохраняю этот делегат в задаче, и она, после выполнения, запускает этот Continuation.
При тестировании выяснилось, что код после await срабатывает не всегда. Происходит это, как я выяснил, потому, что OnCompleted у аваитера в некоторые моменты вызывается уже после того, как задача выполнилась, соответственно, Continuation делать она уже не будет. Понятное дело - это же параллельные потоки. Теперь вопрос - как сделать так, чтобы OnCompleted вызывалось до старта задачи? Как узнать, что вот эта задача должна сначала дождаться вызова метода OnCompleted с передачей делегата? Или как вообще определить, что задача, хоти выполнила свой основной код до конца, должна дождаться еще и continuation-кода? Или как вообще это делают?
Понимаю, что дич сложная, рассчитываю на то, что может кто-то тоже писал TaskModel, и как-то решил данную проблему.
Заранее спасибо.
PS. Да, я знаю про рефлектор, сейчас пока безуспешно пытаюсь понять как это делает оригинальная TaskModel.
Решение задачи: «Собственные задачи»
textual
Листинг программы
class MyAwaiter : INotifyCompletion
{
private Action continuation;
public bool IsCompleted { get; private set; }
public object GetResult() => null;
public void OnCompleted(Action continuation)
{
if (IsCompleted)
continuation(); // Метод вызвался уже после окончания задачи, так что просто вызываем делегат
else
this.continuation = continuation; // Сохраняем на потом
}
public MyAwaiter GetAwaiter()
{
new Thread(RunParallelOperation).Start();
return this;
}
private void RunParallelOperation()
{
// Чего-нибудь делаем...
IsCompleted = true;
// Делегат уже передали?
if (continuation != null)
continuation();
}
}