C# 对azure DownloadToStreamAsync接收的并行任务设置限制

C# 对azure DownloadToStreamAsync接收的并行任务设置限制,c#,azure,task-parallel-library,C#,Azure,Task Parallel Library,我有一大堆文件(大约10k)需要从WindowsAzure存储下载。为了让它们并行下载,而不是一次下载一个,我使用blob DownloadToStreamAsync方法,该方法返回一个任务对象。然后,我使用将流保存到文件的方法设置任务ContinueWith 代码如下: foreach (var File in ServerFiles) { string sFileName = File.Uri.LocalPath.ToString(); CloudBlockBlob oBlo

我有一大堆文件(大约10k)需要从WindowsAzure存储下载。为了让它们并行下载,而不是一次下载一个,我使用blob DownloadToStreamAsync方法,该方法返回一个任务对象。然后,我使用将流保存到文件的方法设置任务ContinueWith

代码如下:

foreach (var File in ServerFiles)
{
    string sFileName = File.Uri.LocalPath.ToString();
    CloudBlockBlob oBlob = BiActionscontainer.GetBlockBlobReference(sFileName.Replace("/" + Container + "/", ""));

    MemoryStream ms = new MemoryStream();
    BlobRequestOptions f = new BlobRequestOptions();
    Task downloadTask = oBlob.DownloadToStreamAsync(ms);

    downloadTask.ContinueWith((Task task) =>
    {
         ms.Position = 0;
         lock(lockObject)
         {
              using (FileStream file = new FileStream(ResultPath, FileMode.Append, FileAccess.Write))
              {
                   byte[] bytes = ms.ToArray();
                   file.Write(bytes, 0, bytes.Length);
              }
         }
         ms.Dispose();
    });
}
此代码是在我们的一台服务器(而不是azure)上运行的工具的一部分-windows 2003 server。问题是,在该服务器上,我得到“操作已超时。windows 2003 standard上的Microsoft.WindowsAzure.Storage”,因此我认为可能是许多文件同时发出请求并阻塞了带宽


所以我想知道,在从第三方库获取任务对象的情况下,如何限制一次运行的并行程序的数量?并且仍然对即将到来的其余任务进行排队?

您可以使用
信号量lim
进行此操作。设置您想要的并发请求数,然后在启动每个请求之前使用
wait WaitAsync()
,在每个请求完成之后使用
Release()
,最后等待剩余的任务

封装在helper方法中,它可能如下所示:

public static async Task ForEachAsync<T>(
    this IEnumerable<T> items, Func<T, Task> action, int maxDegreeOfParallelism)
{
    var semaphore = new SemaphoreSlim(maxDegreeOfParallelism);

    var tasks = new List<Task>();

    foreach (var item in items)
    {
        await semaphore.WaitAsync();

        Func<T, Task> loopAction = async x =>
        {
            await action(x);
            semaphore.Release();
        };

        tasks.Add(loopAction(item));
    }

    await Task.WhenAll(tasks);
}
另一种实现将使用来自TPL数据流的
ActionBlock
。它知道如何在这里完成所有需要的操作,您只需设置它:

public static Task ForEachAsync<T>(
    this IEnumerable<T> items, Func<T, Task> action, int maxDegreeOfParallelism)
{
    var block = new ActionBlock<T>(
        action,
        new ExecutionDataflowBlockOptions
        {
            MaxDegreeOfParallelism = maxDegreeOfParallelism
        });

    foreach (var item in items)
    {
        block.Post(item);
    }

    block.Complete();
    return block.Completion;
}
公共静态任务ForEachAsync(
此IEnumerable items,Func action,int maxDegreeOfParallelism)
{
var block=新动作块(
行动,
新的ExecutionDataflowBlockOptions
{
MaxDegreeOfParallelism=MaxDegreeOfParallelism
});
foreach(项目中的var项目)
{
块.柱(项);
}
block.Complete();
返回块。完成;
}

如果我没有弄错的话,您的系统正在被冲洗,因为下面一行代码
foreach(ServerFiles中的var文件)
。你需要在这里节流。
public static Task ForEachAsync<T>(
    this IEnumerable<T> items, Func<T, Task> action, int maxDegreeOfParallelism)
{
    var block = new ActionBlock<T>(
        action,
        new ExecutionDataflowBlockOptions
        {
            MaxDegreeOfParallelism = maxDegreeOfParallelism
        });

    foreach (var item in items)
    {
        block.Post(item);
    }

    block.Complete();
    return block.Completion;
}