LoginSignup
0
0

More than 5 years have passed since last update.

[.NET] 検証:Task + CancellationTokenSourceでは.NET4.0以前のスレッドの実行をブロックするメソッドを中断できない

Last updated at Posted at 2015-09-17

.NET4.0で導入されたTaskCancellationTokenSourceを使用したとき、.NET4.0以前から存在するスレッドの実行をブロックするメソッドを中断できるかどうかを検証する。

動機 (追記)

.NET4.0(VisualStudio2010)の環境下、Taskでマルチスレッドのアプリケーションをプログラムしながら、ふと思った。
Thread.InterruptならWaitSleepJoin状態のスレッドを中断できるけど、TaskCancellationTokenSourceだとこの状態のスレッドは中断できないよね」
Interruptには、スレッドをブロックするメソッド(Monitor.Waitのような)でThreadInterruptedExceptionを発生させて途中終了させるという機能がある。しかし、Cancelにはそのような機能はない。

……いや、実は知らないだけで、実は裏でそんな機能があるのではないか。Threadを捨ててTaskを使おうと喧伝されているのだから、Taskにも似たような機能がついていてもおかしくない。
検証してみよう。そして、ついでだから、ThreadInterruptedExceptionを発生しないようなメソッドについても調べてみよう。

Monitor.Enter中のスレッド(Task)を中断できるか

実験コード

Program.cs
static void Main(string[] args)
{
    var cancel = new CancellationTokenSource();
    var o = new object();
    var task1 = Task.Factory.StartNew(() =>
    {
        Trace.WriteLine("Task1 - start Monitor.Enter");
        Monitor.Enter(o);
        Thread.Sleep(10000);
        Monitor.Exit(o);
        Trace.WriteLine("Task1 - done Monitor.Exit");
    });

    Thread.Sleep(100);
    var task2 = Task.Factory.StartNew(() =>
    {
        try
        {
            Trace.WriteLine("Task2 - start Monitor.Enter");
            Monitor.Enter(o);
            Trace.WriteLine("Task2 - done Monitor.Enter");
            Thread.Sleep(1000);
        }
        catch (Exception ex)
        {
            Trace.WriteLine(ex.ToString());
            throw ex;
        }
        finally
        {
            Monitor.Exit(o);
            Trace.WriteLine("Task2 - done Monitor.Exit");
        }
    }, cancel.Token);
    task2.ContinueWith(t => Trace.WriteLine(t.Status));

    Thread.Sleep(100);
    cancel.Cancel();
    Trace.WriteLine("Foreground - canceled");

    Console.WriteLine("Press Enter Key...");
    Console.ReadLine();
}

実行結果

Task1 - start Monitor.Enter
Task2 - start Monitor.Enter
Foreground - canceled
Task1 - done Monitor.Exit
Task2 - done Monitor.Enter
Task2 - done Monitor.Exit
RanToCompletion

結果

中断できない。
Monitor.Enter中で中断できないのだから、lockステートメントでオブジェクトのロック取得待ちの状態についても同様と言える。

その他のスレッドをブロックするメソッドはどうか

下記のメソッドについても同様の実験を行う。

  • Monitor.Wait
  • EventWaitHandle.WaitOne
  • AutoResetEvent.WaitOne
  • ManualResetEvent.WaitOne
  • ReaderWriterLock.AcquireWriterLock
  • Thread.Sleep

実験コード

似たようなコードなので省略

結果

どれも中断できない。

結論

実行をブロックするメソッドのうち、CancellationTokenSourceで中断できるのは、引数にCancellationTokenを指定できるメソッドだけである。
これには、以下のメソッドなどが該当する。

  • CountdownEvent.Wait
  • ManualResetEventSlim.Wait
  • Barrier.SignalAndWait
  • BlockingCollection.Add,Take

中断できないメソッドの中には、Thread.Interruptで中断できたメソッド(Monitor.Wait)も含まれる。
そのため、Monitor.Waitを使用したい場合、Threadを使用する意味が依然として存在するように思える。

外部情報

プログラミングC# 第7版の以下のような記述を裏付けることができた。

既に説明した同期メカニズムの中にもCancellationTokenを指定できるものがあります(Windowsの同期プリミティブは.NETのキャンセルモデルをサポートしないため、WaitHandle派生のクラスではサポートされていません。また、Monitorでもキャンセルはサポートされていません。新規に追加されたAPIではサポートされています)。

続き

Monitor.Wait/PulseよりもManualResetEventSlimを使う へと続く。

0
0
1

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
0
0