c# xamarin.ios task-parallel-library async-await long-running-processes

c# - ¿Cuál es la forma correcta de combinar tareas de larga ejecución con un patrón asíncrono/espera?



xamarin.ios task-parallel-library (1)

Tengo una clase de temporizador de "Alta precisión" que necesito para poder iniciar, detener y pausar / reanudar. Para hacer esto, estoy uniendo un par de ejemplos diferentes que encontré en Internet, pero no estoy seguro de si estoy usando Tareas con asnyc / await correctamente.

Aquí está mi código relevante:

//based on http://haukcode.wordpress.com/2013/01/29/high-precision-timer-in-netc/ public class HighPrecisionTimer : IDisposable { Task _task; CancellationTokenSource _cancelSource; //based on http://blogs.msdn.com/b/pfxteam/archive/2013/01/13/cooperatively-pausing-async-methods.aspx PauseTokenSource _pauseSource; Stopwatch _watch; Stopwatch Watch { get { return _watch ?? (_watch = Stopwatch.StartNew()); } } public bool IsPaused { get { return _pauseSource != null && _pauseSource.IsPaused; } private set { if (value) { _pauseSource = new PauseTokenSource(); } else { _pauseSource.IsPaused = false; } } } public bool IsRunning { get { return !IsPaused && _task != null && _task.Status == TaskStatus.Running; } } public void Start() { if (IsPaused) { IsPaused = false; } else if (!IsRunning) { _cancelSource = new CancellationTokenSource(); _task = new Task(ExecuteAsync, _cancelSource.Token, TaskCreationOptions.LongRunning); _task.Start(); } } public void Stop() { if (_cancelSource != null) { _cancelSource.Cancel(); } } public void Pause() { if (!IsPaused) { if (_watch != null) { _watch.Stop(); } } IsPaused = !IsPaused; } async void ExecuteAsync() { while (!_cancelSource.IsCancellationRequested) { if (_pauseSource != null && _pauseSource.IsPaused) { await _pauseSource.Token.WaitWhilePausedAsync(); } // DO CUSTOM TIMER STUFF... } if (_watch != null) { _watch.Stop(); _watch = null; } _cancelSource = null; _pauseSource = null; } public void Dispose() { if (IsRunning) { _cancelSource.Cancel(); } } }

¿Alguien puede, por favor, echar un vistazo y proporcionarme algunos consejos sobre si estoy haciendo esto correctamente?

ACTUALIZAR

He intentado modificar mi código según los comentarios de Noseratio a continuación, pero todavía no puedo entender la sintaxis. Cada intento de pasar el método ExecuteAsync () a TaskFactory.StartNew o Task.Run , genera un error de compilación como el siguiente:

"La llamada es ambigua entre los siguientes métodos o propiedades: TaskFactory.StartNew (Action, CancelToken ...) y TaskFactory.StartNew <Task> (Func <Task>, CancelToken ...)".

Por último, ¿hay una manera de especificar el LongRunning TaskCreationOption sin tener que proporcionar un TaskScheduler?

async **Task** ExecuteAsync() { while (!_cancelSource.IsCancellationRequested) { if (_pauseSource != null && _pauseSource.IsPaused) { await _pauseSource.Token.WaitWhilePausedAsync(); } //... } } public void Start() { //_task = Task.Factory.StartNew(ExecuteAsync, _cancelSource.Token, TaskCreationOptions.LongRunning, null); //_task = Task.Factory.StartNew(ExecuteAsync, _cancelSource.Token); //_task = Task.Run(ExecuteAsync, _cancelSource.Token); }

ACTUALIZACIÓN 2

Creo que he reducido esto, pero todavía no estoy seguro de la sintaxis correcta. ¿Sería esta la manera correcta de crear la tarea de manera que el código de consumidor / llamante continúe, con la tarea girando y comenzando en un nuevo hilo asíncrono?

_task = Task.Run(async () => await ExecuteAsync, _cancelSource.Token); //**OR** _task = Task.Factory.StartNew(async () => await ExecuteAsync, _cancelSource.Token, TaskCreationOptions.LongRunning, TaskScheduler.Default);


Aquí hay algunos puntos:

  • async void métodos de async void solo son buenos para los manejadores de eventos asíncronos ( más información ). Su async void ExecuteAsync() devuelve instantáneamente (tan pronto como el flujo de código alcanza a await _pauseSource dentro de él). Esencialmente, su _task está en el estado completado después de eso, mientras que el resto de ExecuteAsync se ejecutará sin ser observado (porque es void ). Incluso es posible que no continúe ejecutándose en absoluto, dependiendo de cuándo finalice su subproceso principal (y, por lo tanto, el proceso).

  • Dado esto, debe hacerlo async Task ExecuteAsync() , y usar Task.Run o Task.Factory.StartNew lugar de la new Task para iniciarla. Debido a que desea que el método de acción de su tarea sea async , estaría tratando con tareas anidadas aquí, es decir, Task<Task> , que Task.Run desenvolvería automáticamente para usted. Más información se puede encontrar here y here .

  • PauseTokenSource adopta el siguiente enfoque (por diseño, AFAIU): el lado del código del consumidor (el que llama Pause ) en realidad solo solicita una pausa, pero no se sincroniza. Continuará ejecutándose después de Pause , incluso aunque el lado del productor aún no haya alcanzado el estado de espera, es decir, await _pauseSource.Token.WaitWhilePausedAsync() . Esto puede estar bien para la lógica de tu aplicación, pero debes tenerlo en cuenta. Más información here .

[ACTUALIZACIÓN] A continuación se muestra la sintaxis correcta para usar Factory.StartNew . Nota Task<Task> y task.Unwrap . También tenga en cuenta _task.Wait() en Stop , está allí para asegurarse de que la tarea se haya completado cuando se Stop (de manera similar a Thread.Join ). Además, TaskScheduler.Default se utiliza para indicar a Factory.StartNew que use el programador de agrupaciones de hebras. Esto es importante si crea su objeto HighPrecisionTimer desde otra tarea, que a su vez se creó en un hilo con un contexto de sincronización no predeterminado, por ejemplo, un hilo UI (más información here y here ).

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(); } } }