-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)); 

и больше ничего. Таким образом, независимо от того, является ли функция методом расширения вообще не влияет на ее поведение, это просто убеждение программистов не печатать длинную версию из моего вышеприведенного примера.

Что касается безопасности вашего кода, сначала вам нужно понять, что означает «потокобезопасность». Я настоятельно рекомендую вам прочитать статью «What is this thing you call "thread safe"?» Эрика Липперта, это очень поможет вам понять, что означает безопасность потока.

Ваш код не имеет доступа или не изменяет внешние переменные из его области действия, поэтому сама функция является потокобезопасной, но это не значит, что она не может использоваться в «небезопасных» потоках. К счастью, вам повезло, и Task, и TimeSpan гарантируют безопасность во всех своих методах и свойствах, поэтому маловероятно, чтобы вы столкнулись с проблемами безопасности потоков.


Однако, все это, как говорится, у вас есть ошибка с состоянием гонки. Если task.IsCompleted возвращает true, а task выдает исключение, вы никогда не получите уведомление об этом исключении. Также, если timeSpan == Timeout.InfiniteTimeSpan, ваша функция будет немедленно возвращаться с завершенной задачей, даже если переданная задача все еще запущена. Вам нужно выполнить 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

Смежные вопросы