開始、停止、一時停止/再開できるようにする必要がある「高精度」タイマークラスがあります。これを行うために、私はインターネットで見つけたいくつかの異なる例を結び付けていますが、asnyc/awaitでタスクを正しく使用しているかどうかはわかりません。
これが私の関連コードです:
//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();
}
}
}
誰かが見て、私がこれを正しく行っているかどうかについていくつかの指針を教えてもらえますか?
[〜#〜] update [〜#〜]
以下のNoseratioのコメントに従ってコードを変更しようとしましたが、それでも構文を理解できません。 ExecuteAsync()メソッドをTaskFactory.StartNewまたはTask.Runのいずれかに渡そうとするすべての試み、次のようなコンパイルエラーが発生します。
「次のメソッドまたはプロパティ間で呼び出しがあいまいです:TaskFactory.StartNew(Action、CancellationToken ...)とTaskFactory.StartNew <Task>(Func <Task>、CancellationToken ...)」。
最後に、TaskSchedulerを提供せずにLongRunning TaskCreationOptionを指定する方法はありますか?
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);
}
更新2
これを絞り込んだと思いますが、正しい構文についてはまだわかりません。これは、タスクをスピンアップして新しい非同期スレッドで開始し、コンシューマー/呼び出しコードが続行されるようにタスクを作成する正しい方法でしょうか?
_task = Task.Run(async () => await ExecuteAsync, _cancelSource.Token);
//**OR**
_task = Task.Factory.StartNew(async () => await ExecuteAsync, _cancelSource.Token, TaskCreationOptions.LongRunning, TaskScheduler.Default);
ここにいくつかのポイントがあります:
_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
_に注意してください。また、Stop
の_task.Wait()
にも注意してください。これは、Stop
が戻ったときにタスクが完了したことを確認するためのものです(_Thread.Join
_と同様の方法で)。また、_TaskScheduler.Default
_は、スレッドプールスケジューラを使用するように_Factory.StartNew
_に指示するために使用されます。これは、別のタスク内からHighPrecisionTimer
オブジェクトを作成する場合に重要です。このタスクは、デフォルト以外の同期コンテキストを持つスレッドで作成されます。 UIスレッド(詳細 ここ および ここ )。
_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();
}
}
}
_