4

サーバーに対して約 100 回の http API 呼び出しを行い、結果を処理する必要があるプロセスがあります。コマンドのリストを作成し、それらを非同期で実行するこの commandexecutor をまとめました。約 100 回の呼び出しを行い、結果を解析するには 1 分以上かかります。ブラウザを使用して 1 つのリクエストを送信すると、約 100 ミリ秒で応答が返ってきます。〜 100 回の呼び出しは約 10 秒であると考えるでしょう。私は何か間違ったことをしていると信じており、これはもっと速く進むはずです。

 public static class CommandExecutor
 {
    private static readonly ThreadLocal<List<Command>> CommandsToExecute =
        new ThreadLocal<List<Command>>(() => new List<Command>());
    private static readonly ThreadLocal<List<Task<List<Candidate>>>> Tasks =
        new ThreadLocal<List<Task<List<Candidate>>>>(() => new List<Task<List<Candidate>>>());

    public static void ExecuteLater(Command command)
    {
        CommandsToExecute.Value.Add(command);
    }

    public static void StartExecuting()
    {
        foreach (var command in CommandsToExecute.Value)
        {
            Tasks.Value.Add(Task.Factory.StartNew<List<Candidate>>(command.GetResult));
        }

        Task.WaitAll(Tasks.Value.ToArray());
    }

    public static List<Candidate> Result()
    {
        return Tasks.Value.Where(x => x.Result != null)
                          .SelectMany(x => x.Result)
                          .ToList();
    }
}

このリストに渡すコマンドは、新しい httpclient を作成し、そのクライアントで URL を使用して getasync を呼び出し、文字列の応答をオブジェクトに変換してから、フィールドをハイドレートします。

    protected void Initialize()
    {
        _httpClient = new HttpClient();
        _httpClient.DefaultRequestHeaders.Accept.Add(new MediaTypeWithQualityHeaderValue("text/plain"));
    }

    protected override void Execute()
    {
        Initialize();

        var task = _httpClient.GetAsync(string.Format(Url, Input));
        Result = ConvertResponseToObjectAsync(task).Result;
        Result.ForEach(x => x.prop = value);
    }

    private static Task<Model> ConvertResponseToObjectAsync(Task<HttpResponseMessage> task)
    {
        return task.Result.Content.ReadAsAsync<Model>(
           new MediaTypeFormatter[]
           {
                 new Formatter()
           });
    }

私のボトルネックを理解してもらえますか、またはこれをスピードアップする方法について何か提案がありますか?

これらの変更を行う編集により、4秒に短縮されました。

protected override void Execute()
    {
        Initialize();

        _httpClient.GetAsync(string.Format(Url, Input))
        .ContinueWith(httpResponse => ConvertResponseToObjectAsync(httpResponse)
        .ContinueWith(ProcessResult));
    }

    protected void ProcessResult(Task<Model> model)
    {
        Result = model.Result;
        Result.ForEach(x => x.prop = value);
    }
4

2 に答える 2

2

task.ResultConvertResponseToObjectAsyncでの使用を避けてから、でもう一度使用してくださいExecute。代わりに、これらを元のGetAsyncタスクにチェーンしContinueWithます。

現在のところResult、他のタスクが終了するまで現在のスレッドの実行をブロックします。ただし、スレッドプールは、実行する場所がない他のタスクを待機しているタスクによってすぐにバックアップされます。最終的に(1秒待った後)、スレッドプールは実行するスレッドを追加するため、これは最終的に終了しますが、効率はほとんどありません。

Task.Result一般的な原則として、タスクの継続を除いて、アクセスを避ける必要があります。

ボーナスとして、おそらく使用したくないでしょうThreadLocalStorageアクセスされる各スレッドThreadLocalStorageに格納されているアイテムのインスタンスを格納します。この場合、スレッドセーフであるが共有形式のストレージが必要なようです。こういうことをお勧めします。ConcurrentQueue

于 2012-06-28T02:27:09.023 に答える