Ответ 1
Вот несколько моментов:
-
async void
методы хороши только для асинхронных обработчиков событий (подробнее). Вашasync void ExecuteAsync()
возвращается мгновенно (как только поток кода достигнетawait _pauseSource
внутри него). По существу, ваш_task
находится в завершенном состоянии после этого, в то время как остальная частьExecuteAsync
будет выполнена ненаблюдаемой (потому что онаvoid
). Это может даже не продолжаться вообще, в зависимости от того, когда ваш основной поток (и, следовательно, процесс) завершается. -
Учитывая это, вы должны сделать его
async Task ExecuteAsync()
и использоватьTask.Run
илиTask.Factory.StartNew
вместоnew Task
, чтобы запустить его. Поскольку вы хотите, чтобы ваш метод действия задачи былasync
, здесь вы будете иметь дело с вложенными задачами, т.е.Task<Task>
, которыйTask.Run
будет автоматически развернут для вас. Более подробную информацию можно найти здесь и здесь. -
PauseTokenSource
использует следующий подход (по дизайну, AFAIU): потребительская сторона кода (тот, который звонкиPause
) фактически запрашивает паузу, но не синхронизируется с ней. Он будет продолжать выполнение послеPause
, хотя сторона-производитель, возможно, еще не достигла ожидающего состояния, то естьawait _pauseSource.Token.WaitWhilePausedAsync()
. Это может быть нормально для вашей логики приложения, но вы должны знать об этом. Подробнее здесь.
[UPDATE] Ниже приведен правильный синтаксис для использования Factory.StartNew
. Примечание Task<Task>
и task.Unwrap
. Также обратите внимание на _task.Wait()
в Stop
, чтобы убедиться, что задача завершена, когда возвращается Stop
(аналогично Thread.Join
). Кроме того, TaskScheduler.Default
используется для указания Factory.StartNew
использования планировщика пула потоков. Это важно, если вы создаете свой объект HighPrecisionTimer
из другой задачи, которая, в свою очередь, была создана в потоке с контекстом синхронизации по умолчанию, например. (далее здесь и здесь).
using System;
using System.Threading;
using System.Threading.Tasks;
namespace ConsoleApplication
{
public class HighPrecisionTimer
{
Task _task;
CancellationTokenSource _cancelSource;
public void Start()
{
_cancelSource = new CancellationTokenSource();
Task<Task> task = Task.Factory.StartNew(
function: ExecuteAsync,
cancellationToken: _cancelSource.Token,
creationOptions: TaskCreationOptions.LongRunning,
scheduler: TaskScheduler.Default);
_task = task.Unwrap();
}
public void Stop()
{
_cancelSource.Cancel(); // request the cancellation
_task.Wait(); // wait for the task to complete
}
async Task ExecuteAsync()
{
Console.WriteLine("Enter ExecuteAsync");
while (!_cancelSource.IsCancellationRequested)
{
await Task.Delay(42); // for testing
// DO CUSTOM TIMER STUFF...
}
Console.WriteLine("Exit ExecuteAsync");
}
}
class Program
{
public static void Main()
{
var highPrecisionTimer = new HighPrecisionTimer();
Console.WriteLine("Start timer");
highPrecisionTimer.Start();
Thread.Sleep(2000);
Console.WriteLine("Stop timer");
highPrecisionTimer.Stop();
Console.WriteLine("Press Enter to exit...");
Console.ReadLine();
}
}
}