调用Web服务时,C#Tasks和Continuations的最有效配置是什么?

Mal*_*ker 3 .net c# performance threadpool task-parallel-library

我正在创建一个与实时Web API对话的网络客户端.

客户端必须每秒进行多次不同的调用并Task<TResult>返回每个客户端组件,以便客户端可以决定是否阻止:

public Task<TResult> Execute<TResult>(IOperation<TResult> operation);
Run Code Online (Sandbox Code Playgroud)

进行API调用的过程如下所示:

  1. 将(少量,小于1KB)请求序列化为Json
  2. 使用a发送请求 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);
            });
Run Code Online (Sandbox Code Playgroud)

最慢的是这个设置,整个过程在一个任务中执行:

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);
            })
Run Code Online (Sandbox Code Playgroud)

我原以为最后一种方法可能是最快的,因为你为每个请求创建一个后台线程.我的假设是,由于阻塞调用,这不允许TPL最有效地使用可用线程.

那么,是否存在关于应该在任务中应该做什么以及应该在其外部或应该继续进行什么的一般规则?

在这个具体案例中,我可以尝试进一步的优化吗?

Ree*_*sey 7

您根本不需要使用Task.Factory.StartNew,因为已经SendAsync返回Task:

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

这实际上会更有效,因为它根本不需要ThreadPool线程.

请注意,您可以使用async/ 进一步优化它await(以使响应保持异步):

var response = await this.client.SendAsync(requestMessage);
var jsonString = await response.Content.ReadAsStringAsync();
return JsonConvert.DeserializeObject(jsonString);
Run Code Online (Sandbox Code Playgroud)

这也可以通过TPL延续来编写,但这需要返回(解包)任务ReadAsStringAsync,然后在其上发布新的延续以获得最终字符串.