いくつかの結果を生成するボタンがあるWinformsアプリケーションについて考えてみます。ユーザーがボタンを2回押すと、最初のリクエストをキャンセルして結果を生成し、新しいリクエストを開始する必要があります。
以下のパターンを使用していますが、競合状態を防ぐためにコードの一部が必要かどうかはわかりません(コメントアウトされた行を参照)。
private CancellationTokenSource m_cts;
private void generateResultsButton_Click(object sender, EventArgs e)
{
// Cancel the current generation of results if necessary
if (m_cts != null)
m_cts.Cancel();
m_cts = new CancellationTokenSource();
CancellationToken ct = m_cts.Token;
// **Edit** Clearing out the label
m_label.Text = String.Empty;
// **Edit**
Task<int> task = Task.Run(() =>
{
// Code here to generate results.
return 0;
}, ct);
task.ContinueWith(t =>
{
// Is this code necessary to prevent a race condition?
// if (ct.IsCancellationRequested)
// return;
int result = t.Result;
m_label.Text = result.ToString();
}, ct, TaskContinuationOptions.OnlyOnRanToCompletion, TaskScheduler.FromCurrentSynchronizationContext());
}
知らせ:
CancellationTokenSource
メインスレッドでのみキャンセルします。CancellationToken
継続では、元のタスクと同じものを使用します。
次の一連のイベントが可能かどうか疑問に思っています。
- ユーザーが「結果の生成」ボタンをクリックします。初期タスクt1が開始されます。
- ユーザーは「結果の生成」ボタンをもう一度クリックします。Windowsメッセージはキューに送信されますが、ハンドラーはまだ実行されていません。
- タスクt1が終了します。
- TPL startは、継続を開始する準備をします(
まだキャンセルCancellationToken
されていないため)。タスクスケジューラは、作業をWindowsメッセージキューに送信します(メインスレッドで実行するため)。 - 2回目のクリックのgenerateResultsButton_Clickが実行を開始し、
CancellationTokenSource
がキャンセルされます。 - 継続作業が開始され、トークンがキャンセルされなかったかのように動作します(つまり、UIに結果が表示されます)。
だから、私は質問が要約すると思います:
作業がメインスレッドに投稿されると(を使用してTaskScheduler.FromCurrentSynchronizationContext()
)、TPLCancellationToken
はタスクのアクションを実行する前にメインスレッドでをチェックしますか、またはそれが起こっているスレッドでキャンセルトークンをチェックしてから、作業をに投稿しますSynchronizationContext
?