Как реализовать отмену общих Task:s в C#

Все, вот вопрос о дизайне/лучших практиках сложного случая отмены Task:s в C#. Как реализовать отмену общей задачи?

В качестве минимального примера предположим следующее; у нас есть длительная, совместно отменяемая операция «Работа». Он принимает токен отмены в качестве аргумента и выдает исключение, если он был отменен.Он работает с некоторым состоянием приложения и возвращает значение. Его результат независимо требуется двум компонентам пользовательского интерфейса.

Пока состояние приложения не изменяется, значение функции Work должно быть кэшировано, и если одно вычисление продолжается, новый запрос не должен запускать второе вычисление, а должен начать ожидать результата.

Любой из компонентов пользовательского интерфейса должен иметь возможность отменить свою задачу, не влияя на задачу других компонентов пользовательского интерфейса.

Вы со мной до сих пор?

Вышеупомянутое может быть выполнено путем введения кэша задач, который упаковывает реальную рабочую задачу в TaskCompletionSources, чьи задачи затем возвращаются компонентам пользовательского интерфейса. Если компонент пользовательского интерфейса отменяет свою задачу, он отменяет только задачу TaskCompletionSource, а не основную задачу. Это все хорошо. Компоненты пользовательского интерфейса создают CancellationSource, и запрос на отмену представляет собой обычный нисходящий дизайн с взаимодействующей задачей TaskCompletionSource внизу.

Теперь к настоящей проблеме. Что делать при изменении состояния приложения? Предположим, что работа функции «Работа» с копией состояния невозможна.

Одним из решений может быть прослушивание изменений состояния в кэше задач (или около того). Если в кеше есть CancellationToken, используемый базовой задачей, выполняющей функцию Work, он может ее отменить. Затем это может вызвать отмену всех прикрепленных задач TaskCompletionSources, и, таким образом, оба компонента пользовательского интерфейса получат задачи Canceled. Это какая-то отмена снизу вверх.

Есть ли предпочтительный способ сделать это? Есть ли шаблон проектирования, который описывает это где-то?

Отмену снизу вверх можно реализовать, но это выглядит немного странно. Задача пользовательского интерфейса создается с помощью CancellationToken, но отменяется из-за другого (внутреннего) CancellationToken. Кроме того, поскольку токены не совпадают, OperationCancelledException нельзя просто игнорировать в пользовательском интерфейсе — это (в конечном итоге) приведет к возникновению исключения во внешнем финализаторе Task:s.

6
задан 4ZM 21 May 2012 в 15:36
поделиться