Task/asynс/await и прерывание синхронного метода

Shocker.Pro
Дата: 04.07.2014 13:44:21
Что-то никак не врублюсь, как убить синхронный метод.

Делаю "отзывчивый" интерфейс winforms с возможностью пользователю прервать выполнение задачи.
В приведенном примере метод SyncLock имитирует долгий синхронный метод, внутрь которого я влезть не могу.

Если пользоваться по старинке потоками, я его спокойно убиваю через thread.Abort(), обрабатываю исключение прерывания и спокойно завершаю задачу.
В примерах с async/await приводится опрос токена, но я ведь не могу его проводить внутри чужого синхронного метода. Подскажите, где я не догоняю?

    private CancellationTokenSource cts;


    private void btStart_Click(object sender, EventArgs e)
    {
      StartAsync();
    }
    private void btStop_Click(object sender, EventArgs e)
    {
      cts.Cancel();
    }

    private async void StartAsync()
    {
      cts = new CancellationTokenSource();
      await Tasker(cts.Token);
    }
    
    private Task Tasker(CancellationToken ct)
    {
      return Task.Run(new Action(() =>
      {
        // какие-то действия
        SyncLock();
        // какие-то другие действия
      }), ct);
    }

    private void SyncLock()
    {
      Thread.Sleep(5000);
    }
Алексей К
Дата: 04.07.2014 14:59:43
Основных варианта два:

1. Если асинхронная операция самописная - в одном из циклов обращаться к CancellationToken на предмет отмены.

2. Если асинхронная операция содержит вложенную асинхронную операция (асинхронное обращение к БД, сокету и т. п.) - передавать CancellationToken во вложенную асинхронную операцию.
Shocker.Pro
Дата: 04.07.2014 15:08:04
Еще раз.
у меня есть СИНХРОННАЯ чужая операция.
Я не могу влезть к ней внутрь.
В примере подобную операцию представляет SyncLock

Вопрос конкретно по примеру. Можно ли прервать выполнение SyncLock до его реального завершения (не внося изменения в SyncLock) используя работу с Task-ами?
Алексей К
Дата: 04.07.2014 15:12:17
Shocker.Pro
Еще раз.
у меня есть СИНХРОННАЯ чужая операция.
Я не могу влезть к ней внутрь.
Тогда никак. Только Thread.Abort.
Shocker.Pro
Дата: 04.07.2014 15:28:31
Получается тогда ерунда? Сделали Task, наворотили сахарных конструкций, при этом потерялась возможность насильно прервать выполнение?
pation
Дата: 04.07.2014 15:54:29
Shocker.Pro
Получается тогда ерунда? Сделали Task, наворотили сахарных конструкций, при этом потерялась возможность насильно прервать выполнение?

ерунду сморозил, ничто не потерялось, а Thread.Abort - зло
cdtyjv
Дата: 04.07.2014 15:56:58
Shocker.Pro
Еще раз.
у меня есть СИНХРОННАЯ чужая операция.
Я не могу влезть к ней внутрь.
В примере подобную операцию представляет SyncLock

Вопрос конкретно по примеру. Можно ли прервать выполнение SyncLock до его реального завершения (не внося изменения в SyncLock) используя работу с Task-ами?
У вас принципиальное непонимание того, как работает программа. Поток - это набор инструкций. Его нельзя взять, и остановить в произвольном месте. Любая операция по нормальной остановке какой-либо задачи выглядит следующем образом:
1) Когда вам надо остановить задачу, вы выставляете где-то флажок, что надо остановиться.
2) Сама задача периодически проверяет этот флажок, и если видит, что надо остановиться - останавливается.
Принципиально так реализовано все. Так реализованы прерывания IO операций, так реализованы прерывания через CancellationToken, так реализованы прерывания ожидания всяких критических секций и условий, и т.д.. Разумеется, конечная реализация может отличаться - где-то это просто managed-переменная, где-то вызов какого-нибудь WinAPI, где-то вызов прерывания, и т.д.. Но суть одна - "первый поток ставит флажок, второй поток читает флажок".

Вы же прерываете поток через Abort(). Это плохая, отвратительная практика. В Java, например, это вообще запрещено. И правильно, что запрещено - это дурной тон! А что, если ваш поток, например, сидел внутри критической секции и должен был консистентно изменить две переменные, а вы вызвали Abort, и он смог изменить только одну, оставив систему в неконсистентном состоянии? Abort() используют либо от лени, либо от непонимания.

Если вас по каким-то причинам не устраивает cancellation token, то сделайте какой-нибудь volatile bool - один поток выставляет его, второй читает.
Shocker.Pro
Дата: 04.07.2014 16:05:06
Ну так при прерывании потока возникает эксепшен, вызывается финализатор, где я могу привести все в правильное состояние (в моем случае нужно только вернуть кнопку Start в состояние Enabled, а кнопку Stop - в disabled).

cdtyjv
Если вас по каким-то причинам не устраивает cancellation token, то сделайте какой-нибудь volatile bool - один поток выставляет его, второй читает.
как мне дать возможность пользователю прервать синхронную операцию, нажав кнопку stop?
Алексей К
Дата: 04.07.2014 17:02:12
Shocker.Pro
Получается тогда ерунда? Сделали Task, наворотили сахарных конструкций, при этом потерялась возможность насильно прервать выполнение?
А она была, если забыть о thread.abort?
Shocker.Pro
Дата: 04.07.2014 17:10:02
А почему о нем надо забыть?
Еще раз. Как мне дать возможность пользователю прервать синхронную операцию, нажав кнопку stop?