C# で await/async を使ってみた。 何故か常に Task.IsCompleted == true になる。 動いているか、の判定に使えないので、困った。 dotNet4.5 で。
// ----------------------------------------
// MainWindow.xaml.cs
//#define BY_THE_SLEEP
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using System.Windows;
using System.Windows.Controls;
using System.Windows.Data;
using System.Windows.Documents;
using System.Windows.Input;
using System.Windows.Media;
using System.Windows.Media.Imaging;
using System.Windows.Navigation;
using System.Windows.Shapes;
namespace WpfAppTestOnDotNet45
{
/// <summary>
/// MainWindow.xaml の相互作用ロジック
/// </summary>
public partial class MainWindow : Window
{
public MainWindow()
{
InitializeComponent();
}
//
// Task.IsCompleted が常に true を返すようです。
// そのため完了しているか否かは volatile 変数などで手動で管理する必要がありそうです。
//
private Task taskForCancelTestAsync = null;
private System.Threading.CancellationTokenSource ctsForCancelTestAsync = null;
private void btnCancelForCancelTestAsync_Click(object sender, RoutedEventArgs e)
{
var tsk = taskForCancelTestAsync;
var cts = ctsForCancelTestAsync;
if (tsk == null || cts == null)
{
MessageBox.Show("実行されていません。");
return;
}
else if (tsk != null && (tsk.IsCanceled || /* tsk.IsCompleted || */ tsk.IsFaulted))
{
// 常に tsk.IsCompleted == true になります。
// また tsk.Status == TaskStatus.RanToCompletion になっています。
// よって
// Cancel 出来るか、とか、Runできるか、といった判定に使えないようです。
MessageBox.Show("既に完了しています。");
return;
}
else if (cts != null && cts.IsCancellationRequested)
{
MessageBox.Show("キャンセル中です。");
return;
}
else
{
//MessageBox.Show("実行中です。");
//return;
}
try
{
var syncContextUI = System.Threading.Tasks.TaskScheduler.FromCurrentSynchronizationContext();
#if true
tsk.ContinueWith((curTask) => {
MessageBox.Show("キャンセルできました。");
}, syncContextUI);
cts.Cancel();
#endif
#if false
// これは「Cancel 時に実行」という意図があるが、MessageBox.Showが動かない。
var cts2nd = new System.Threading.CancellationTokenSource();
var ctoken2nd = cts2nd.Token;
tsk.ContinueWith((curTask) => {
MessageBox.Show("キャンセルできました。[CancelOnly]");
}, ctoken2nd, TaskContinuationOptions.OnlyOnCanceled, syncContextUI);
cts.Cancel();
#endif
}
catch (TaskCanceledException ex)
{
// たぶんキャンセルする側のスレッドで呼ばれる例外。
// Sleep のときも Task.Delay のときも呼ばれなかった。
MessageBox.Show("[btnCancel_Click]Error: TaskCanceledException");
Console.WriteLine("[btnCancel_Click]TaskCanceledException:" + ex.ToString());
}
catch (OperationCanceledException ex)
{
// たぶんキャンセルされたスレッドで呼ばれる例外。
// Sleep のときも Task.Delay のときも呼ばれなかった。
MessageBox.Show("[btnCancel_Click]Error: OperationCanceledException");
Console.WriteLine("[btnCancel_Click]OperationCanceledException:" + ex.ToString());
}
catch (AggregateException exg)
{
// Sleep のときも Task.Delay のときも呼ばれなかった。
MessageBox.Show("[btnCancel_Click]Error: AggregateException");
Console.WriteLine("[btnCancel_Click]AggregateException:" + exg.ToString());
foreach (Exception ex in exg.InnerExceptions)
{
Console.WriteLine("\t" + ex.ToString());
}
}
catch (Exception ex)
{
// Sleep のときも Task.Delay のときも呼ばれなかった。
MessageBox.Show("[btnCancel_Click]Error: OperationCanceledException");
Console.WriteLine("[btnCancel_Click]Exception:" + ex.ToString());
throw;
}
}
private void btnRunForCancelTestAsync_Click(object sender, RoutedEventArgs e)
{
var tsk = taskForCancelTestAsync;
var cts = ctsForCancelTestAsync;
if (tsk == null || cts == null)
{
//MessageBox.Show("実行されていません。");
//return;
}
else if (tsk != null && (tsk.IsCanceled || tsk.IsCompleted || tsk.IsFaulted))
{
//MessageBox.Show("既に完了しています。");
//return;
}
else if (cts != null && cts.IsCancellationRequested)
{
MessageBox.Show("キャンセル中です。");
return;
}
else
{
MessageBox.Show("実行中です。");
return;
}
if (tsk != null)
{
tsk.Dispose();
tsk = null;
}
if (cts != null)
{
cts.Dispose();
cts = null;
}
taskForCancelTestAsync = null;
ctsForCancelTestAsync = null;
try
{
}
catch (Exception ex)
{
MessageBox.Show("[btnRun_Click]Error: OperationCanceledException");
Console.WriteLine("[btnRun_Click]Exception:" + ex.ToString());
throw;
}
ctsForCancelTestAsync = new System.Threading.CancellationTokenSource();
var ctoken = ctsForCancelTestAsync.Token;
#if BY_THE_SLEEP
taskForCancelTestAsync = System.Threading.Tasks.Task.Factory.StartNew(
() =>
{
CancelTestForAsync.Run(ctoken);
}, ctoken);
#else
taskForCancelTestAsync = System.Threading.Tasks.Task.Factory.StartNew(
async () =>
{
await CancelTestForAsync.Run(ctoken);
}, ctoken);
#endif
}
}
}
// ----------------------------------------
// CancelTestForAsync.cs
//#define BY_THE_SLEEP
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
namespace WpfAppTestOnDotNet45
{
class CancelTestForAsync
{
#if BY_THE_SLEEP
public static void Run(System.Threading.CancellationToken ctoken) // ← Sleep の場合の定義。
#else
public static async Task Run(System.Threading.CancellationToken ctoken) // ← Delay の場合の定義。
#endif
{
Console.WriteLine("[CancelTestForAsync::Run]Start");
for (int i = 0; i < 20; i++)
{
try
{
#if BY_THE_SLEEP
System.Threading.Thread.Sleep(millisecondsTimeout: 20 * 1000);
#else
// Task.Delay(millisecondsDelay: 20 * 1000, cancellationToken: ctoken);
// のように await が無い場合は、別スレッドを実行した後、すぐに続行するので意味が無いことに注意。
await Task.Delay(millisecondsDelay: 20 * 1000, cancellationToken: ctoken);
#endif
if (ctoken.IsCancellationRequested)
{
// Sleep の場合、ここに来る。 System.Threading.CancellationToken では Cancel 即座のキャンセルが出来ない。
Console.WriteLine("[CancelTestForAsync::Run]ctoken.ThrowIfCancellationRequested();");
ctoken.ThrowIfCancellationRequested();
}
Console.WriteLine("[CancelTestForAsync::Run]Delayed");
}
catch (TaskCanceledException ex)
{
// たぶんキャンセルする側のスレッドで呼ばれる例外。
// Sleep の場合、想定どおり、ここに来ない。
// Task.Delay の場合、ここに来る。 await の中で Cancel が検知されると、ここに来るということ。
Console.WriteLine("[CancelTestForAsync::Run]TaskCanceledException:" + ex.ToString());
throw;
}
catch (OperationCanceledException ex)
{
// たぶんキャンセルされたスレッドで呼ばれる例外。
// Sleep の場合、想定どおり、ここで発生。 ctoken.ThrowIfCancellationRequested(); で呼ばれる、ということ。
Console.WriteLine("[CancelTestForAsync::Run]OperationCanceledException:" + ex.ToString());
throw;
}
catch (AggregateException exg)
{
// Sleep の場合、想定どおり、ここに来ない。
Console.WriteLine("[CancelTestForAsync::Run]AggregateException:" + exg.ToString());
foreach (Exception ex in exg.InnerExceptions)
{
Console.WriteLine("\t" + ex.ToString());
}
throw;
}
catch (Exception ex)
{
// Sleep の場合、想定どおり、ここに来ない。
Console.WriteLine("[CancelTestForAsync::Run]Exception:" + ex.ToString());
throw;
}
}
}
}
}
// ----------------------------------------
0 件のコメント:
コメントを投稿