批量运行异步任务

时间:2017-02-28 14:16:08

标签: c# asynchronous async-await task-parallel-library

我正在异步运行一个存储过程(我需要运行相同的SP大约150次),如下所示: -

var queryTask = new List<Task>();
for (int i = 0; i < 150; i++)
{
      queryTask.Add(da.ExecuteSPAsync("Async" + i.ToString()));
}
Task.WhenAll(queryTask).Wait();

现在,它将创建150 Tasks并执行它们。我可以批量拆分这些任务并运行它们吗?这会减少SQL服务器端的负载吗?

或者我应该考虑TPL来运行吗?像这样: -

Parallel.For(0, 150, new ParallelOptions { MaxDegreeOfParallelism = 5 },
              x => da.ExecuteSP("PPWith5Threads" + x.ToString()));

哪一个在性能方面更好?这只是一个用于演示目的的示例,实际上我有一个自定义类型的集合,我需要在其上执行一些SP。

1 个答案:

答案 0 :(得分:4)

所以你可以使用信号量。信号量背后的概念是夜总会保镖场景,其中保镖对俱乐部(线程池)中允许的人数(线程)有限制,并且人们离开(线程完成)其他人可以进入(线程可以继续) ),达到极限。

所有线程都将被启动,但是WaitAsync()会阻止线程继续运行。 Release()表示线程重新进入线程池。

这里的延迟给出了批处理的效果,因为每个线程大致等待相同的时间,但实际上,您更有可能一次看到几个。

用随机的int代替Delay(5000)以更好看。

class Program
{
    static void Main(string[] args)
    {
        var runner = new SprocRunner(new DataAccess());

        var threads = new List<Task>();
        for (var i = 0; i < 150; i++)
        {
            threads.Add(runner.ExecuteSp($"Async {i}"));
        }

        Task.WaitAll(threads.ToArray());
    }
}

public class SprocRunner
{
    private readonly System.Threading.SemaphoreSlim batcher = new System.Threading.SemaphoreSlim(10, 10);
    private readonly DataAccess da;

    public SprocRunner(DataAccess da)
    {
        this.da = da;
    }

    public async Task ExecuteSp(string asyncTaskName)
    {
        await batcher.WaitAsync();

        try
        {
            await this.da.ExecuteSP(asyncTaskName);
        }
        catch (Exception e)
        {
        }
        finally
        {
            batcher.Release();
        }
    }
}

public class DataAccess
{
    public Task ExecuteSP(string name)
    {
        Console.WriteLine(name);

        return Task.Delay(5000);
    }
}

为什么不使用Parallel

在阅读了Stephen Toub之类的论文后,案例是,如果您正在执行大量I / O绑定任务,那么在某些情况下使用Parallel并不是问题而且它确实允许您把工作做完。要考虑的事情是线程创建不是一个不可忽视的成本,如果你请求的线程多于ThreadPool中的线程,它将不得不注入新的线程。如果你在一个充分利用像ASP.NET这样的线程的环境中,这就成了一个问题。在I / O工作中阻塞大量线程非常糟糕,可以使您的服务器停滞不前。

这是使用Task抽象的地方,因为你可以运行所有这些任务然后等待I / O响应 - 但非常重要的是 - 他们不会阻止任何线程(等待结果的主线程除外),只有在I / O完成后才会简单地使用它来处理结果。