24

基本的にデータベースとの対話と初期化を繰り返し試みる非同期コードを作成する必要があります。多くの場合、最初の試行は失敗するため、再試行する必要があります。

昔は、次のようなパターンを使用していました。

void WaitForItToWork()
{
    bool succeeded = false;
    while (!succeeded)
    {
        // do work
        succeeded = outcome; // if it worked, mark as succeeded, else retry
        Threading.Thread.Sleep(1000); // arbitrary sleep
    }
}

最近、非同期パターンに関して .NET に多くの変更が加えられたことに気付いたので、私の質問は本当にこれが使用する最良の方法であるか、それとも調査中に価値があるasyncかということasyncです。

アップデート

明確にするために、サービスのコンストラクターで生成されるため、生成するメソッドが終了するのを待つ必要がないように、この作業を非同期で生成したいので、コンストラクターは即座に戻る必要があります。

4

4 に答える 4

37

そのフラグメントを次のようにリファクタリングできます。

async Task<bool> WaitForItToWork()
{
    bool succeeded = false;
    while (!succeeded)
    {
        // do work
        succeeded = outcome; // if it worked, make as succeeded, else retry
        await Task.Delay(1000); // arbitrary delay
    }
    return succeeded;
}

どうやら、それが与える唯一の利点は、スレッドプールをより効率的に使用することです。これは、遅延を発生させるのに常にスレッド全体が必要であるとは限らないためです。

の取得方法によっては、outcomeを使用してこの仕事を完了するためのはるかに効率的な方法がある場合がありますasync/awaitGetOutcomeAsync()多くの場合、自然な方法で Web サービス、データベース、またはソケット呼び出しを非同期に行うようなものがあるかもしれませんvar outcome = await GetOutcomeAsync()

WaitForItToWorkコンパイラによって部分に分割され、await行からの部分が非同期で続行されることを考慮することが重要です。これがおそらく、内部でどのように行われるかについての最良の説明です。問題は、通常、コードのある時点で、非同期タスクの結果を同期する必要があるということです。例えば:

private void Form1_Load(object sender, EventArgs e)
{
    Task<bool> task = WaitForItToWork();
    task.ContinueWith(_ => {
        MessageBox.Show("WaitForItToWork done:" + task.Result.toString()); // true or false
    }, TaskScheduler.FromCurrentSynchronizationContext());
}

あなたは単にこれを行うことができました:

private async void Form1_Load(object sender, EventArgs e)
{
    bool result = await WaitForItToWork();
    MessageBox.Show("WaitForItToWork done:" + result.toString()); // true or false
}

ただし、それはForm1_Load非同期メソッドにもなります。

[アップデート]

以下は、async/awaitこの場合に実際に何が行われるかを説明するための私の試みです。同じロジックの 2 つのバージョンWaitForItToWorkAsync( を使用async/await) とWaitForItToWorkAsyncTap( を使用せずにTAP パターンasync/awaitを使用) を作成しました。最初のバージョンは、2 番目のものとは異なり、非常に簡単です。したがって、async/await主にコンパイラの構文糖衣ですが、非同期コードの記述と理解がはるかに簡単になります。

// fake outcome() method for testing
bool outcome() { return new Random().Next(0, 99) > 50; }

// with async/await
async Task<bool> WaitForItToWorkAsync()
{
    var succeeded = false;
    while (!succeeded)
    {
        succeeded = outcome(); // if it worked, make as succeeded, else retry
        await Task.Delay(1000);
    }
    return succeeded;
}

// without async/await
Task<bool> WaitForItToWorkAsyncTap()
{
    var context = TaskScheduler.FromCurrentSynchronizationContext();
    var tcs = new TaskCompletionSource<bool>();
    var succeeded = false;
    Action closure = null;

    closure = delegate
    {
        succeeded = outcome(); // if it worked, make as succeeded, else retry
        Task.Delay(1000).ContinueWith(delegate
        {
            if (succeeded)
                tcs.SetResult(succeeded);
            else
                closure();
        }, context);
    };

    // start the task logic synchronously
    // it could end synchronously too! (e.g, if we used 'Task.Delay(0)')
    closure();

    return tcs.Task;
}

// start both tasks and handle the completion of each asynchronously
private void StartWaitForItToWork()
{
    WaitForItToWorkAsync().ContinueWith((t) =>
    {
        MessageBox.Show("WaitForItToWorkAsync complete: " + t.Result.ToString());
    }, TaskScheduler.FromCurrentSynchronizationContext());

    WaitForItToWorkAsyncTap().ContinueWith((t) =>
    {
        MessageBox.Show("WaitForItToWorkAsyncTap complete: " + t.Result.ToString());
    }, TaskScheduler.FromCurrentSynchronizationContext());
}

// await for each tasks (StartWaitForItToWorkAsync itself is async)
private async Task StartWaitForItToWorkAsync()
{
    bool result = await WaitForItToWorkAsync();
    MessageBox.Show("WaitForItToWorkAsync complete: " + result.ToString());

    result = await WaitForItToWorkAsyncTap();
    MessageBox.Show("WaitForItToWorkAsyncTap complete: " + result.ToString());
}

スレッド化について一言。ここで明示的に作成された追加のスレッドはありません。内部的には、実装はプール スレッドを使用する場合があります (タイマー キューTask.Delay()を使用すると思われます) が、この特定の例 (WinForms アプリ) では、後の継続は同じ UI スレッドで発生します。他の実行環境 (コンソール アプリなど) では、別のスレッドで続行される場合があります。IMO、 Stephen Cleary によるこの記事は、スレッド化の概念を理解するために必読です。awaitasync/await

于 2013-09-03T08:46:45.493 に答える
2

別の解決策を提供するだけです

public static void WaitForCondition(Func<bool> predict)
    {
        Task.Delay(TimeSpan.FromMilliseconds(1000)).ContinueWith(_ =>
        {
            var result = predict();
            // the condition result is false, and we need to wait again.
            if (result == false)
            {
                WaitForCondition(predict);
            }
        });
    }
于 2016-05-06T08:03:11.167 に答える
2

タスクが非同期の場合は、次を試すことができます。

    async Task WaitForItToWork()
    {
        await Task.Run(() =>
        {
            bool succeeded = false;
            while (!succeeded)
            {
                // do work
                succeeded = outcome; // if it worked, make as succeeded, else retry
                System.Threading.Thread.Sleep(1000); // arbitrary sleep
            }
        });
    }

http://msdn.microsoft.com/en-us/library/hh195051.aspxを参照してください。

于 2013-09-03T08:51:45.073 に答える