2

を使用しないこのコードは、await次のようにコンパイルされます。

IEnumerable<PingResponse> pingResponses;
using (var prestoWcf = new PrestoWcf<IPingService>())
{
    pingResponses = prestoWcf.Service.GetAllForPingRequest(this.PingRequest);
}

foreach (PingResponse response in pingResponses) { // code here }

を使用したこのコードawaitはコンパイルされません。

IEnumerable<PingResponse> pingResponses;
await Task.Factory.StartNew(() =>
{
    using (var prestoWcf = new PrestoWcf<IPingService>())
    {
        pingResponses = prestoWcf.Service.GetAllForPingRequest(this.PingRequest);
    }
});

foreach (PingResponse response in pingResponses) { // code here }

エラーは次のとおりです。Use of unassigned local variable 'pingResponses'

async/await を導入するとこの問題が発生するのはなぜですか?

4

2 に答える 2

9

それが機能しない理由は、メソッドに提供されたデリゲートがループStartNewの前に常に実行されることをコンパイラが認識できないためです。foreachあなたはそれを知っています、そして私はそれを知っていますが、コンパイラはそれが現在の明確な代入可能性規則で あることを証明することはできません.

コンパイラをだましてこれを実行させるための「回避策」がいくつかありますが、最善かつ最も慣用的な解決策は、閉じられたオーバー変数を変更するのではなく、タスクに結果を返すようにすることです。そうすれば、タスクの副作用ではなく、結果自体に依存することになります。これにより、コードの推論が容易になり (タスクとそれを使用するコードは、それぞれの実装が互いに依存するのではなく、個別に分析できます)、異なるスレッド間で共有されるメモリの適切な同期が保証されます (重要なタスク)。 .

実際のコードに関しては、 dcastro の回答で既に提供されています。

IEnumerable<PingResponse> pingResponses = 
    await Task.Factory.StartNew(() =>
    {
        using (var prestoWcf = new PrestoWcf<IPingService>())
        {
            return prestoWcf.Service.GetAllForPingRequest(this.PingRequest);
        }
    });

さらに良いことに、SLack の提案に従って、スレッド プール スレッドで同期メソッドを使用するのではなく、適切に非同期のメソッドを使用できます。そうすることで、オペレーティング システムの機能を利用して、ネットワーク要求の完了を通知することができます。その際、スレッド プールをそのままにしておくことでリソースを浪費する必要がなくなります。

于 2013-11-01T17:14:10.660 に答える
3

StartNewオブジェクトを返すことができるオーバーロードを 使用します: http://msdn.microsoft.com/en-us/library/dd321455(v=vs.110).aspx

IEnumerable<PingResponse> pingResponses = 
await Task.Factory.StartNew(() =>
{
    using (var prestoWcf = new PrestoWcf<IPingService>())
    {
        return prestoWcf.Service.GetAllForPingRequest(this.PingRequest);
    }
});
于 2013-11-01T17:09:43.027 に答える