2016-01-22 5 views
1

Реализация метода ExecuteNonQueryAsync() в System.Data.SqlClient.SqlCommand выглядит следующим образом:TaskCompletionSource использование методов IO Async

public override Task<int> ExecuteNonQueryAsync(CancellationToken cancellationToken) { 

     Bid.CorrelationTrace("<sc.SqlCommand.ExecuteNonQueryAsync|API|Correlation> ObjectID%d#, ActivityID %ls\n", ObjectID); 
     SqlConnection.ExecutePermission.Demand(); 

     TaskCompletionSource<int> source = new TaskCompletionSource<int>(); 

     CancellationTokenRegistration registration = new CancellationTokenRegistration(); 
     if (cancellationToken.CanBeCanceled) { 
      if (cancellationToken.IsCancellationRequested) { 
       source.SetCanceled(); 
       return source.Task; 
      } 
      registration = cancellationToken.Register(CancelIgnoreFailure); 
     } 

     Task<int> returnedTask = source.Task; 
     try { 
      RegisterForConnectionCloseNotification(ref returnedTask); 

      Task<int>.Factory.FromAsync(BeginExecuteNonQueryAsync, EndExecuteNonQueryAsync, null).ContinueWith((t) => { 
       registration.Dispose(); 
       if (t.IsFaulted) { 
        Exception e = t.Exception.InnerException; 
        source.SetException(e); 
       } 
       else { 
        if (t.IsCanceled) { 
         source.SetCanceled(); 
        } 
        else { 
         source.SetResult(t.Result); 
        } 
       } 
      }, TaskScheduler.Default); 
     } 
     catch (Exception e) { 
      source.SetException(e); 
     } 

     return returnedTask; 
    } 

который я хотел бы резюмировать следующим образом:

  1. Создать TaskCompletionSource<int> source = new TaskCompletionSource<int>();
  2. Создать новый с использованием Task<int>.Factory.FromAsync, используя API APM «Begin/End»
  3. Вызов source.SetResult(), когда задача завершается.
  4. Вернуться source.Task

Какой смысл использовать TaskCompletionSource здесь и почему бы не вернуть задачу, созданную Task<int>.Factory.FromAsync() напрямую? У этой задачи также есть результат и исключение (если есть).

В C# в книге кратко, в асинхронном программировании и продолжениях раздела, в нем говорится:

При написании задержки, мы использовал TaskCompletionSource, который является стандартным способом реализации «нижнего уровня " Асинхронные методы с привязкой к вводу/выводу.

Для методов, основанных на вычислении, мы используем Task.Run, чтобы инициировать параллелизм, связанный с потоком. Просто возвращая задачу вызывающему, мы создаем асинхронный метод.

Почему методы, связанные с вычислением, могут быть реализованы с использованием Task.Run(), но не связаны с методами ввода-вывода?

ответ

3

Обратите внимание, что для окончательного ответа вам нужно будет попросить автора кода. Не говоря уже о том, что мы можем только догадываться. Тем не менее, я думаю, что разумно делать некоторые выводы с разумной точностью & hellip;

В чем смысл использования TaskCompletionSource здесь и почему бы не вернуть задачу, созданную Task.Factory.FromAsync() напрямую?

В этом случае, мне кажется, что основной причиной является возможность реализации отменить регистрацию зарегистрированного обратного вызова CancelIgnoreFailure() перед задачей фактически завершена. Это гарантирует, что к моменту, когда клиентский код получит уведомление о завершении, сам API полностью очистился от операции.

Второй причиной может быть просто обеспечение полной абстракции. То есть не разрешать любой базовой реализации для «утечки» из метода в виде объекта Task, который вызывающий может проверять или (хуже) манипулировать таким образом, который мешает правильной и надежной работе задачи.

Почему методы, связанные с вычислением могут быть реализованы с использованием Task.Run(), но не связаны с методами ввода-вывода?

Вы можете осуществлять I/O связанных операций с использованием Task.Run(), но зачем? Выполнение этой операции приводит к потоку операции, которая для операции, которая иначе не требует потока, является расточительной.

Операции с привязкой к вводу-выводу, как правило, имеют поддержку от порта завершения ввода-вывода и пула потоков IOCP (потоки обработки дескрипторов произвольно большого количества IOCP), и поэтому более эффективно просто использовать существующие асинхронный API ввода-вывода, а не использовать Task.Run() для вызова синхронного метода ввода-вывода.

+0

Могу ли я сказать, что использование TaskCompletionSource на самом деле должно унифицировать вывод метода, поэтому исключение сглаживается, результат устанавливается и т. Д. В этом случае многопроцессорная механика внутри метода может быть чем угодно. – Helic

+0

_ «механизм резьбы внутри метода может быть любым» _ - до тех пор, пока метод работает асинхронно, да. Использование TCS здесь позволяет публичному методу быть _abstraction_ того, что на самом деле происходит внутри, гарантируя, что вызывающий абонент не зависит от конкретных сведений о реализации (например, хороших практик реализации). –