211

私が読んだすべてのブログ投稿では、C# で非同期メソッドを使用する方法について説明していますが、何らかの理由で、独自の非同期メソッドを作成して使用する方法については説明していません。だから私は今、私のメソッドを消費するこのコードを持っています:

private async void button1_Click(object sender, EventArgs e)
{
    var now = await CountToAsync(1000);
    label1.Text = now.ToString();
}

そして、私はこのメソッドを書きましたCountToAsync:

private Task<DateTime> CountToAsync(int num = 1000)
{
    return Task.Factory.StartNew(() =>
    {
        for (int i = 0; i < num; i++)
        {
            Console.WriteLine("#{0}", i);
        }
    }).ContinueWith(x => DateTime.Now);
}

これはTask.Factory、非同期メソッドを記述するための最良の方法ですか、それとも別の方法で記述する必要がありますか?

4

3 に答える 3

250

StartNewそのレベルの複雑さが必要でない限り、お勧めしません。

非同期メソッドが他の非同期メソッドに依存している場合、最も簡単な方法は次のasyncキーワードを使用することです。

private static async Task<DateTime> CountToAsync(int num = 10)
{
  for (int i = 0; i < num; i++)
  {
    await Task.Delay(TimeSpan.FromSeconds(1));
  }

  return DateTime.Now;
}

非同期メソッドが CPU 作業を行っている場合は、次を使用する必要がありますTask.Run

private static async Task<DateTime> CountToAsync(int num = 10)
{
  await Task.Run(() => ...);
  return DateTime.Now;
}

私のasync/awaitイントロが役に立つかもしれません。

于 2013-04-17T15:30:59.980 に答える
14

メソッド内で async/await を使用したくないが、外部から await キーワードを使用できるように「装飾」する場合は、TaskCompletionSource.cs :

public static Task<T> RunAsync<T>(Func<T> function)
{ 
    if (function == null) throw new ArgumentNullException(“function”); 
    var tcs = new TaskCompletionSource<T>(); 
    ThreadPool.QueueUserWorkItem(_ =>          
    { 
        try 
        {  
           T result = function(); 
           tcs.SetResult(result);  
        } 
        catch(Exception exc) { tcs.SetException(exc); } 
   }); 
   return tcs.Task; 
}

あちらこちらから_

タスクでこのようなパラダイムをサポートするには、タスク ファサードを保持し、任意の非同期操作をタスクとして参照する方法が必要ですが、そのタスクを提供している基盤となるインフラストラクチャのルールに従って、そのタスクの有効期間を制御する必要があります。非同期であり、それほどコストがかからない方法で実行します。これが TaskCompletionSource の目的です。

WebClient.csなどの .NET ソースでも使用されていることがわかりました。

    [HostProtection(ExternalThreading = true)]
    [ComVisible(false)]
    public Task<string> UploadStringTaskAsync(Uri address, string method, string data)
    {
        // Create the task to be returned
        var tcs = new TaskCompletionSource<string>(address);

        // Setup the callback event handler
        UploadStringCompletedEventHandler handler = null;
        handler = (sender, e) => HandleCompletion(tcs, e, (args) => args.Result, handler, (webClient, completion) => webClient.UploadStringCompleted -= completion);
        this.UploadStringCompleted += handler;

        // Start the async operation.
        try { this.UploadStringAsync(address, method, data, tcs); }
        catch
        {
            this.UploadStringCompleted -= handler;
            throw;
        }

        // Return the task that represents the async operation
        return tcs.Task;
    }

最後に、次のことも役に立ちました。

私はいつもこの質問を受けます。つまり、外部リソースへの I/O 呼び出しをブロックしているスレッドがどこかにあるはずです。つまり、非同期コードは要求スレッドを解放しますが、それはシステム内の他の場所にある別のスレッドを犠牲にするだけですよね? いいえ、まったくありません。

非同期要求がスケーリングされる理由を理解するために、非同期 I/O 呼び出しの (簡略化された) 例をトレースします。リクエストがファイルに書き込む必要があるとしましょう。要求スレッドは、非同期書き込みメソッドを呼び出します。WriteAsync は Base Class Library (BCL) によって実装され、非同期 I/O に完了ポートを使用します。そのため、WriteAsync 呼び出しは、非同期ファイル書き込みとして OS に渡されます。次に、OS はドライバー スタックと通信し、データを渡して I/O 要求パケット (IRP) に書き込みます。

ここが興味深いところです。デバイス ドライバーが IRP をすぐに処理できない場合は、非同期的に処理する必要があります。そのため、ドライバーはディスクに書き込みを開始するように指示し、OS に「保留中」の応答を返します。OS はその「保留中」の応答を BCL に渡し、BCL は未完了のタスクを要求処理コードに返します。リクエスト処理コードはタスクを待機し、タスクはそのメソッドから不完全なタスクを返します。最後に、要求処理コードが ASP.NET に不完全なタスクを返し、要求スレッドが解放されてスレッド プールに戻ります。

ASP.NET での非同期/待機の概要

目標が (応答性ではなく) スケーラビリティの向上である場合、それを行う機会を提供する外部 I/O の存在にすべて依存します。

于 2016-11-02T10:33:19.410 に答える