-2

私は自分の頭を拡張メソッドの周りにラップするのに苦労しています。これらは静的クラス内の静的メソッドです。彼らはどのように内部的に初期化されますか?たとえば、私は以下の拡張メソッドを書いた。スレッドセーフですか?この拡張メソッドは安全ですか?

public static async Task TimeoutAfter(this Task task, TimeSpan timeSpan) 
{ 
    var cts = new CancellationTokenSource(); 
    try 
    { 
     if (task.IsCompleted || timeSpan == Timeout.InfiniteTimeSpan) 
      return; 
     if (timeSpan == TimeSpan.Zero) 
      throw new TimeoutException(); 
     if (task == await Task.WhenAny(task, Task.Delay(timeSpan, cts.Token))) 
     { 
      cts.Cancel(); 
      await task; 
     } 
     else 
     { 
      throw new TimeoutException(); 
     } 
    } 
    finally 
    { 
     cts.Dispose(); 
    } 
} 
+4

「内部で初期化された」とはどういう意味ですか?メソッドは「初期化された」ものではありません。 –

答えて

3

すべての拡張メソッドはないが

var result = ExtensionMethodClass.TimeoutAfter(myTask, TimeSpan.FromSecconds(5)); 

何もないので

var result = myTask.TimeoutAfter(TimeSpan.FromSecconds(5)); 

を回しています。したがって、関数が拡張メソッドであるかどうかに関わらず、その動作にはまったく影響はありません。プログラマが上記の例から長いバージョンをタイプする必要はないというだけのことです。

コードがスレッドセーフであるかどうかについては、まず「スレッドセーフ」が何を意味するのかを理解する必要があります。 Eric Lippertの記事「What is this thing you call "thread safe"?」を読むことを強くお勧めします。これは、スレッドの安全性の意味を理解するのに大いに役立ちます。

コードは外部変数をスコープからアクセスまたは変更しないため、関数自体はスレッドセーフですが、スレッドセーフではありません。幸いなことに、TaskTimeSpanは、すべてのメソッドとプロパティで安全性が保証されているため、スレッドセーフの問題が発生する可能性は低いです。


しかし、言われていることはすべて競合状態のバグです。 task.IsCompletedがtrueを返し、taskが例外をスローすると、その例外は通知されません。 timeSpan == Timeout.InfiniteTimeSpanあなたの関数は、渡されたタスクがまだ実行中であっても、immedatly完了したタスクを返します。タイムアウトを計画していなくても、awaitタスクが必要です。あなたはまだそれを行っていない場合にも、あなたの試みは/最後に、using声明

public static async Task TimeoutAfter(this Task task, TimeSpan timeSpan) 
{ 
    using(var cts = new CancellationTokenSource()) 
    { 
     if (task.IsCompleted || timeSpan == Timeout.InfiniteTimeSpan) 
     { 
      await task; 
      return; 
     } 
     if (timeSpan == TimeSpan.Zero) 
      throw new TimeoutException(); 
     if (task == await Task.WhenAny(task, Task.Delay(timeSpan, cts.Token))) 
     { 
      cts.Cancel(); 
      await task; 
     } 
     else 
     { 
      throw new TimeoutException(); 
     } 
    } 
} 

最後に簡体することができますが、そのタスクをタイムアウトしたい場合には、あなたもTask<T>にかかるバージョンを作成したいと思うでしょう結果を返します。

public static async Task<T> TimeoutAfter<T>(this Task<T> task, TimeSpan timeSpan) 
{ 
    using(var cts = new CancellationTokenSource()) 
    { 
     if (task.IsCompleted || timeSpan == Timeout.InfiniteTimeSpan) 
     { 
      return await task 
     } 
     if (timeSpan == TimeSpan.Zero) 
      throw new TimeoutException(); 
     if (task == await Task.WhenAny(task, Task.Delay(timeSpan, cts.Token))) 
     { 
      cts.Cancel(); 
      return await task; 
     } 
     else 
     { 
      throw new TimeoutException(); 
     } 
    } 
} 
+0

私のコードを最適化するだけでなく、素晴らしい答えをありがとう! – gebs

関連する問題