2016-08-18 118 views
-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 = myTask.TimeoutAfter(TimeSpan.FromSecconds(5)); 

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

,别无其他。因此,函数是否是扩展方法根本不会影响它的行为,这只是让程序员不必从上面的例子中输入长版本的说服。

至于如果您的代码是线程安全的,首先您需要了解“线程安全”的含义。我强烈建议您阅读Eric Lippert撰写的文章“What is this thing you call "thread safe"?”,它将极大地帮助您了解线程安全的含义。

您的代码不访问或改变其范围内的任何外部变量,因此该函数本身是线程安全的,但这并不意味着它不能用于“线程不安全”的方式。幸运的是,您很幸运,TaskTimeSpan都确保您的所有方法和属性都具有安全性,因此您不太可能遇到任何线程安全问题。


然而所有这一切,你有一个错误的竞争条件。如果task.IsCompleted返回true并且task引发异常,您将永远不会收到该异常的通知。同样,如果timeSpan == Timeout.InfiniteTimeSpan即使传入的任务仍在运行,你的函数也会立即返回一个已完成的任务。即使您不打算超时,您也需要await任务。此外,你的try /终于可以simplifed到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