限制并发异步任务

时间:2019-03-21 19:45:31

标签: c# async-await semaphore

我想使用SSH.NET库和Renci.SshNet.Async扩展名将潜在的大批文件(可能是100个)上传到FTP。我需要将并发上传的数量限制为五个,或者我发现FTP可以处理的任何数量。

这是我的代码,没有任何限制:

using (var sftp = new SftpClient(sftpHost, 22, sftpUser, sftpPass))
{
    var tasks = new List<Task>();
    try
    {
        sftp.Connect();

        foreach (var file in Directory.EnumerateFiles(localPath, "*.xml"))
        {
            tasks.Add(
                sftp.UploadAsync(
                    File.OpenRead(file),      // Stream input
                    Path.GetFileName(file),   // string path
                    true));                   // bool canOverride
        }

        await Task.WhenAll(tasks);
        sftp.Disconnect();
    }
    // trimmed catch
}

我已经读过SemaphoreSlim,但是我不完全了解它是如何工作的以及如何与TAP一起使用。这是基于MSDN documentation的实现方式。

我不确定使用Task.Run是否是正确的方法,因为它受I / O限制,据我所知,Task.Run是用于CPU约束的工作,而{{ 1}} / async用于I / O绑定工作。我也不理解这些任务如何进入信号量(是正确的术语)信号量,因为它们所做的只是在其上调用await

.Release()

1 个答案:

答案 0 :(得分:1)

  

据我所知,Task.Run用于CPU绑定工作

正确。

  

并异步/等待绑定到I / O的工作。

不。 await是用于将延续添加到异步操作的工具。 它不在乎异步操作是什么性质。它简单地使组合任何类型的异步操作 更加容易。

如果您想将几个异步操作组合在一起,则可以通过使用各种异步操作制作async方法,在需要它们的结果(或完成它们)时await对其进行编排。 ),然后使用Task形式的方法作为其自己的新异步操作。

在您的情况下,您的新异步操作只需要等待信号量,上传文件然后释放信号量即可。

async Task UploadFile()
{
    await semaphore.WaitAsync();
    try
    {
        await sftp.UploadAsync(
            File.OpenRead(file),
            Path.GetFileName(file),
            true));   
    }
    finally
    {
        semaphore.Release();
    }
}

现在,您只需为每个文件调用该方法即可。

另外,由于这是常见的操作,因此您可能会发现create a new class to handle this logic值得这样做,这样您就可以简单地创建一个队列并将项目添加到该队列中,并让它在内部处理节流,而不是在您使用它的任何地方复制该机制。