执行多个并发任务的最佳方法是什么?

时间:2014-03-01 04:54:12

标签: c# asp.net task-parallel-library asp.net-web-api2

我一直在阅读异步编程的好处。优势似乎太大了,不能继续忽视它。因此,我决定进入asyncs,任务和等待的土地。我创建了一个简单的类来对我的Web Api执行多个并发POST操作。

通话方式:

...

int tasks = 100;
for (var i = 0; i < tasks ; i++)
{
     int taskNumber = i + 1;
     var task = Task.Run(() => Post(taskNumber , address, client));
}

System.Console.ReadKey();

要运行的任务:

private static async Task Post(int taskNumber, Uri address, HttpClient client)
{
        System.Console.WriteLine("Started Task#: {0}", taskNumber);
        Stopwatch sw = new Stopwatch();
        sw.Start();
        var status = GetDeviceStatus();

        var response = await client.PostAsJsonAsync<DeviceStatus>(address, status);
        sw.Stop();

        if (response.IsSuccessStatusCode)
        {
            Uri statusUri = response.Headers.Location;
            System.Console.WriteLine("{0}- Elapsed: {1} Path: {2}", taskNumber, sw.Elapsed, statusUri.ToString());
        }
        else
        {
            System.Console.WriteLine("{0}- Elapsed: {1} Path: {2}", taskNumber, sw.Elapsed, response.ToString());                
        }
    }

代码按预期工作;然而,这是执行此任务的最佳方式(请原谅双关语)?任何建议都将不胜感激。

2 个答案:

答案 0 :(得分:1)

我建议您使用TAP进行基于I / O的并发,并使用TPL进行基于CPU的并发。由于WebAPI调用是I / O绑定的,因此您应该使用TAP而不是TPL。

在这种情况下无需拨打Task.Run

int requests = 100;
for (var i = 0; i < requests; i++)
{
  int taskNumber = i + 1;
  var task = PostAsync(taskNumber, address, client);
}

您还可以使用Task.WhenAll进行基于TAP的加入:

int requests = 100;
var tasks = Enumerable.Range(0, requests).Select(x => PostAsync(x + 1, address, client));
await Task.WhenAll(tasks);

此外,HttpClient主要是线程安全的。详情见its MSDN page

答案 1 :(得分:0)

我将采用的方法是将Microsoft Reactive Framework用于此类并行代码而不是任务。

这就是我要做的事情:

var query =
    from n in Observable.Range(0, 100)
    from r in Observable.Using(
        () => new HttpClient(),
        client =>
            Observable
                .Start(() =>
                {
                    var status = GetDeviceStatus();
                    return new
                    {
                        Response = client
                            .PostAsJson<DeviceStatus>(address, status),
                        Status = status,
                    };
                }))
    select new
    {
        Number = n,
        r.Response,
        r.Status,
    };

由于这是一个查询,因此在以某种方式调用查询之前它不会运行。方法如下:

query.Subscribe(x =>
{
    /* Do something with
        x.Number,
        x.Response,
        x.Status,
    */
});

这都是在线程池上处理的。所有的同步都是为您完成的。我可以将所有结果作为匿名对象整齐地返回。

没有完美的方法来做这种事情,但我喜欢这种方法。