3

リアルタイムWebAPIと通信するネットワーククライアントを作成しています。

クライアントは、1秒あたり多くの異なる呼び出しを行い、Task<TResult>各クライアントコンポーネントにフィードバックして、クライアントがブロックするかどうかを決定できるようにする必要があります。

public Task<TResult> Execute<TResult>(IOperation<TResult> operation);

API呼び出しを行うプロセスは、次のように実行されます。

  1. (小さい、1KB未満)リクエストをJsonにシリアル化します
  2. を使用してリクエストを送信しますHttpClient
  3. 成功したら、デシリアライズしてTResult(Jsonのサイズは数百KBになる可能性がありますが、通常ははるかに小さくなります)、

私のテストでは、タスクワークフロー内のどこに各ステップを含めるか(したがって、どのスレッドに含めるか)を選択すると、パフォーマンスに大きな影響があります。

私がこれまでに見つけた最速のセットアップはこれです(半疑似コード、簡潔にするためにジェネリック型パラメーターを省略):

// serialize on main thread
var requestString = JsonConvert.SerializeObject(request);
// create message - omitted
var post = Task.Factory.StartNew(() => this.client.SendAsync(requestMessage)).Unwrap();
return post.ContinueWith(response =>
            {
                var jsonString = response.Result.Content.ReadAsStringAsync();
                return JsonConvert.DeserializeObject(jsonString.Result);
            });

最も遅いのは、プロセス全体が単一のタスク内で実行されるこのセットアップです。

return Task.Factory.StartNew((request) => 
            {
                var requestString = JsonConvert.SerializeObject(request);
                // create message - omitted
                var post = client.SendAsync(requestMessage);
                var jsonString = post.Result.Content.ReadAsStringAsync();
                return JsonConvert.DeserializeObject(jsonString.Result);
            })

リクエストごとに1つのバックグラウンドスレッドを作成するため、最後の方法が最も速いと思いました。私の仮定では、これでは、呼び出しがブロックされているため、TPLが使用可能なスレッドを最も効率的に使用することはできません。

それで、タスクに何を入れるべきか、タスクの外に何を置くべきか、または継続するかについての一般的なルールはありますか?

この特定のケースでは、私が試すことができるさらなる最適化はありますか?

4

2 に答える 2

7

すでに:Task.Factory.StartNewSendAsync返すので、使用する必要はまったくありません。Task

var post = this.client.SendAsync(requestMessage);
return post.ContinueWith(response =>
        {
            var jsonString = response.Result.Content.ReadAsStringAsync();
            return JsonConvert.DeserializeObject(jsonString.Result);
        });

ThreadPoolスレッドをまったく必要としないため、これは実際にはより効率的です。

async/を使用してこれをさらに最適化できることに注意してくださいawait(応答を非同期に保つため)。

var response = await this.client.SendAsync(requestMessage);
var jsonString = await response.Content.ReadAsStringAsync();
return JsonConvert.DeserializeObject(jsonString);

これはTPL継続を介して書き込むこともできますが、これには、から(ラップされていない)タスクを返しReadAsStringAsync、最後の文字列を取得するために新しい継続を投稿する必要があります。

于 2013-03-04T18:27:47.143 に答える
2

StartNewしたがって、最初に、最初のリクエストを送信するときに使用する理由はありません。既にタスクがあります。バックグラウンド スレッドでタスクを開始するのは不要なオーバーヘッドです。

したがって、この:

var post = Task.Factory.StartNew(() => this.client.SendAsync(requestMessage)).Unwrap();

になることができる:

var post = this.client.SendAsync(requestMessage);

次に、どちらの場合も、ReadAsStringAsync非同期で処理するのではなく、メソッドの結果をブロックしています。これは、何もせずにそこに座っている別のスレッドプールスレッドを噛み砕いています。

代わりに次のようにします。

return post.ContinueWith(response =>
    {
        return response.Result.Content.ReadAsStringAsync()
            .ContinueWith(t => JsonConvert.DeserializeObject(t.Result));
    }).UnWrap();
于 2013-03-04T18:31:09.143 に答える