web-dev-qa-db-ja.com

ステートメントを使用する際の非同期メソッド

注:UnityでC#を使用しています。つまり、バージョン.NET .5であるため、awaitまたはasyncキーワードを使用できません。

ステートメントを使用動作するメソッドを挿入するとどうなりますか非同期

_using (WebClient wc = new WebClient()) {
    wc.DownloadFileAsync(urlUri, outputFile);
}
SomeMethod1();
SomeMethod2();
_

ご存知のように、メソッドDownloadFileAsync()が呼び出された後、SomeMethod1()がまだ-である間にusingブロックの外にあるDownloadFileAsync()が呼び出されます。 作業中。そのため、この場合、usingステートメントと非同期メソッドがどうなるか本当に混乱しています。

wcDispose()は、問題なく適切なタイミングで呼び出されますか?

そうでない場合、この例を修正するにはどうすればよいですか?

25
Jenix

コメントから:

では、どうすればこれを回避できますか? awaitキーワードを追加するだけですか?

いいえ、ただそれを行うことはできません。 (そのため、以前に提案された重複質問は実際には重複していませんでした…シナリオは微妙に異なります。)ダウンロードが完了するまで破棄を遅らせる必要がありますが、これはさらに2つのプログラムステートメントを実行する必要があるため複雑です(少なくとも… 良い、最小完全コード例 )なしで確実に知ることは不可能です。

doは、待機可能な WebClient.DownloadFileTaskAsync() メソッドに切り替える必要があると思います。これにより、少なくとも実装が簡素化され、 usingステートメントを保持します。

返されたTaskオブジェクトをキャプチャし、他のプログラムステートメントが実行されるまで待機しないことで、問題の他の部分に対処できます。

_using (WebClient wc = new WebClient()) {
    Task task = wc.DownloadFileTaskAsync(urlUri, outputFile);
    SomeMethod1();
    SomeMethod2();
    await task;
}
_

このようにして、ダウンロードを開始し、他の2つのメソッドを呼び出し、次にコードはダウンロードの完了を待ちます。完了した場合にのみ、usingブロックが終了し、WebClientオブジェクトを破棄できるようになります。

もちろん、現在の実装では、間違いなく適切なDownloadXXXCompletedイベントを処理しています。必要に応じて、その方法でオブジェクトを引き続き使用できます。ただし、IMHOは、awaitの使用に切り替えた後、操作の完了時に実行する必要のあるコードをawaitの後に置く方がはるかに優れています。これにより、操作に関連するすべてのコードが1か所に保持され、実装が簡素化されます。


何らかの理由でawaitを使用できない場合は、WebClientの破棄を遅らせるための代替メカニズムを使用する必要があります。 usingを引き続き使用できるアプローチもあれば、DownloadXXXCompletedイベントハンドラーでDispose()を呼び出す必要があるアプローチもあります。より完全なコード例と、awaitが適切でない理由の明確な説明がなければ、最良の代替案が何であるかを確実に言うことはできません。


編集:

現在のコードではawaitにアクセスできないことを確認したので、古いコードと互換性のある他のオプションをいくつか示します…

1つの可能性は、操作を開始した後、同じスレッドで待機することです。

_using (WebClient wc = new WebClient()) {
    object waitObject = new object();
    lock (waitObject)
    {
        wc.DownloadFileCompleted += (sender, e) =>
        {
            lock (waitObject) Monitor.Pulse(waitObject);
        };
        wc.DownloadFileAsync(urlUri, outputFile);
        SomeMethod1();
        SomeMethod2();
        Monitor.Wait(waitObject);
    }
}
_

(注:ManualResetEventCountdownEvent、さらにはSemaphoreおよび/または「スリム」な同等物など、上記の適切な同期を使用できます。単純さと効率のためにMonitorを使用します。当然のことながら、読者は好みに合わせて調整できます。同期の手段。Monitor以外のotherを好む明らかな理由の1つは、他のタイプの同期手法では、DownloadFileCompletedイベントハンドラー自体が待機をブロックするリスクがないことです。 SomeMethod1()メソッドとSomeMethod2()メソッドを完了します。これが重要かどうかは、もちろん、ファイルのダウンロードと比較して、これらのメソッド呼び出しにかかる時間によって異なります。)

ただし、上記は現在のスレッドをブロックします。場合によってはこれで問題ないこともありますが、ほとんどの場合、操作はUIスレッドで開始されており、操作の間、そのスレッドをブロックしないでください。その場合は、usingを完全に省略し、完了イベントハンドラーからDispose()を呼び出すだけです。

_WebClient wc = new WebClient();
wc.DownloadFileCompleted += (sender, e) =>
{
    wc.Dispose();
};
wc.DownloadFileAsync(urlUri, outputFile);
SomeMethod1();
SomeMethod2();
_
22
Peter Duniho

System.Net.WebClientはイベントDownloadFileCompletedを提供します。そのイベントのハンドラーを追加し、その時点でクライアントを破棄できます。

6
pilotcam