C# 限制任务并行库中的线程数

C# 限制任务并行库中的线程数,c#,.net,task-parallel-library,azure-storage,c#-5.0,C#,.net,Task Parallel Library,Azure Storage,C# 5.0,我有数百个文件需要上传到Azure Blob存储。 我想使用并行任务库。 但是,与其在foreach on文件列表中运行所有要上传的100个线程,我如何限制它可以使用的最大线程数并并行完成任务。 还是自动平衡?您是否尝试使用MaxDegreeOfParallelism?像这样: System.Threading.Tasks.Parallel.Invoke( new Tasks.ParallelOptions {MaxDegreeOfParallelism = 5 }, actionsArray

我有数百个文件需要上传到Azure Blob存储。
我想使用并行任务库。
但是,与其在foreach on文件列表中运行所有要上传的100个线程,我如何限制它可以使用的最大线程数并并行完成任务。
还是自动平衡?您是否尝试使用MaxDegreeOfParallelism?像这样:

System.Threading.Tasks.Parallel.Invoke(
new Tasks.ParallelOptions {MaxDegreeOfParallelism =  5 }, actionsArray)

您可以通过运行以下命令找到答案:

class Program
{
    static void Main(string[] args)
    {
        var list = new List<int>();

        for (int i = 0; i < 100; i++)
        {
            list.Add(i);
        }

        var runningIndex = 0;

        Task.Factory.StartNew(() => Action(ref runningIndex));

        Parallel.ForEach(list, i =>
        {
            runningIndex ++;
            Console.WriteLine(i);
            Thread.Sleep(3000);
        });

        Console.ReadKey();
    }

    private static void Action(ref int number)
    {
        while (true)
        {
            Console.WriteLine("worked through {0}", number);
            Thread.Sleep(2900);
        }
    }
}
类程序
{
静态void Main(字符串[]参数)
{
var list=新列表();
对于(int i=0;i<100;i++)
{
列表.添加(i);
}
var runningIndex=0;
Task.Factory.StartNew(()=>Action(ref-runningIndex));
Parallel.ForEach(列表,i=>
{
runningIndex++;
控制台写入线(i);
睡眠(3000);
});
Console.ReadKey();
}
专用静态无效操作(参考整数编号)
{
while(true)
{
WriteLine(“通过{0}工作”,编号);
睡眠(2900);
}
}
}

正如您所看到的,并行性的数量在开始时变小,然后变大,最后变小。因此,肯定有某种自动优化在进行。

您根本不应该为此使用线程。为此,有一个基于
任务
的API,它自然是异步的:。将它与
async/await
SemaphoreSlim
一起使用,以限制并行上载的数量

示例(未测试):

const MAX_PARALLEL_UPLOADS=5;
异步任务上载文件()
{
var files=新列表();
//…将文件添加到列表中
//初始化blob块并
//异步上载文件
使用(var blobBlock=newcloudblockblob(url、凭据))
使用(var信号量=新信号量lim(最大并行上传))
{
var任务=文件。选择(异步(文件名)=>
{
wait semaphore.WaitAsync();
尝试
{
等待blobBlock.UploadFromFileAsync(文件名,FileMode.Create);
}
最后
{
semaphore.Release();
}
}).ToArray();
等待任务。何时(任务);
}
}

基本上,您需要为每个要上载的文件创建一个操作或任务,将它们放在一个列表中,然后处理该列表,限制可以并行处理的数量

var listOfActions = new List<Action>();
foreach (var file in files)
{
    var localFile = file;
    // Note that we create the Task here, but do not start it.
    listOfTasks.Add(new Task(() => blobBlock.UploadFromFileAsync(localFile, FileMode.Create)));
}

var options = new ParallelOptions {MaxDegreeOfParallelism = 5};
Parallel.Invoke(options, listOfActions.ToArray());
演示如何使用任务和操作执行此操作,并提供一个示例项目,您可以下载并运行该项目来查看这两个操作

用行动 如果使用操作,则可以使用内置的.Net Parallel.Invoke函数。这里我们限制它最多并行运行5个线程

var listOfActions = new List<Action>();
foreach (var file in files)
{
    var localFile = file;
    // Note that we create the Task here, but do not start it.
    listOfTasks.Add(new Task(() => blobBlock.UploadFromFileAsync(localFile, FileMode.Create)));
}

var options = new ParallelOptions {MaxDegreeOfParallelism = 5};
Parallel.Invoke(options, listOfActions.ToArray());
var listOfActions=new List();
foreach(文件中的var文件)
{
var localFile=file;
//注意,我们在这里创建任务,但不启动它。
添加(新任务(()=>blobBlock.UploadFromFileAsync(localFile,FileMode.Create));
}
var options=new ParallelOptions{maxdegreeofpparallelism=5};
Parallel.Invoke(options,listOfActions.ToArray());
但是,此选项没有利用UploadFromFileAsync的异步特性,因此您可能需要使用下面的任务示例

有任务 对于任务,没有内置函数。但是,你可以使用我在我的博客上提供的

    /// <summary>
    /// Starts the given tasks and waits for them to complete. This will run, at most, the specified number of tasks in parallel.
    /// <para>NOTE: If one of the given tasks has already been started, an exception will be thrown.</para>
    /// </summary>
    /// <param name="tasksToRun">The tasks to run.</param>
    /// <param name="maxTasksToRunInParallel">The maximum number of tasks to run in parallel.</param>
    /// <param name="cancellationToken">The cancellation token.</param>
    public static async Task StartAndWaitAllThrottledAsync(IEnumerable<Task> tasksToRun, int maxTasksToRunInParallel, CancellationToken cancellationToken = new CancellationToken())
    {
        await StartAndWaitAllThrottledAsync(tasksToRun, maxTasksToRunInParallel, -1, cancellationToken);
    }

    /// <summary>
    /// Starts the given tasks and waits for them to complete. This will run the specified number of tasks in parallel.
    /// <para>NOTE: If a timeout is reached before the Task completes, another Task may be started, potentially running more than the specified maximum allowed.</para>
    /// <para>NOTE: If one of the given tasks has already been started, an exception will be thrown.</para>
    /// </summary>
    /// <param name="tasksToRun">The tasks to run.</param>
    /// <param name="maxTasksToRunInParallel">The maximum number of tasks to run in parallel.</param>
    /// <param name="timeoutInMilliseconds">The maximum milliseconds we should allow the max tasks to run in parallel before allowing another task to start. Specify -1 to wait indefinitely.</param>
    /// <param name="cancellationToken">The cancellation token.</param>
    public static async Task StartAndWaitAllThrottledAsync(IEnumerable<Task> tasksToRun, int maxTasksToRunInParallel, int timeoutInMilliseconds, CancellationToken cancellationToken = new CancellationToken())
    {
        // Convert to a list of tasks so that we don't enumerate over it multiple times needlessly.
        var tasks = tasksToRun.ToList();

        using (var throttler = new SemaphoreSlim(maxTasksToRunInParallel))
        {
            var postTaskTasks = new List<Task>();

            // Have each task notify the throttler when it completes so that it decrements the number of tasks currently running.
            tasks.ForEach(t => postTaskTasks.Add(t.ContinueWith(tsk => throttler.Release())));

            // Start running each task.
            foreach (var task in tasks)
            {
                // Increment the number of tasks currently running and wait if too many are running.
                await throttler.WaitAsync(timeoutInMilliseconds, cancellationToken);

                cancellationToken.ThrowIfCancellationRequested();
                task.Start();
            }

            // Wait for all of the provided tasks to complete.
            // We wait on the list of "post" tasks instead of the original tasks, otherwise there is a potential race condition where the throttler's using block is exited before some Tasks have had their "post" action completed, which references the throttler, resulting in an exception due to accessing a disposed object.
            await Task.WhenAll(postTaskTasks.ToArray());
        }
    }
//
///启动给定的任务并等待它们完成。这将最多并行运行指定数量的任务。
///注意:如果给定任务之一已启动,将引发异常。
/// 
///要运行的任务。
///并行运行的最大任务数。
///取消令牌。
公共静态异步任务StartAndWaitAllThrottledAsync(IEnumerable tasksToRun,int-MaxTaskStorUnparallel,CancellationToken CancellationToken=new CancellationToken())
{
wait start和wait allthrottledasync(tasksToRun,maxstaskstoruninparallel,-1,cancellationToken);
}
/// 
///启动给定的任务并等待它们完成。这将并行运行指定数量的任务。
///注意:如果在任务完成之前达到超时,则可能会启动另一个任务,可能会超过指定的最大允许运行时间。
///注意:如果给定任务之一已启动,将引发异常。
/// 
///要运行的任务。
///并行运行的最大任务数。
///在允许另一个任务启动之前,我们应该允许max任务并行运行的最长毫秒数。指定-1无限期等待。
///取消令牌。
公共静态异步任务StartAndWaitAllThrottledAsync(IEnumerable tasksToRun,int-MaxTaskStorUnparallel,int-TimeoutInMilleds,CancellationToken CancellationToken=new CancellationToken())
{
//转换为任务列表,这样我们就不会不必要地对其进行多次枚举。
var tasks=tasksToRun.ToList();
使用(var节流器=新信号量LIM(MaxTaskStorUninPallel))
{
var postTaskTasks=新列表();
//让每个任务在完成时通知节流器,以减少当前正在运行的任务数。
tasks.ForEach(t=>postTaskTasks.Add(t.ContinueWith(tsk=>throttler.Release()));
//开始运行每个任务。
foreach(任务中的var任务)
{
//增加当前正在运行的任务数,如果正在运行的任务太多,请等待。
wait throttler.WaitAsync(timeoutin毫秒,cancellationToken);
cancellationToken.ThrowIfCancellationRequested();
task.Start();
}
//等待提供的所有任务完成。
//我们等待“post”任务列表,而不是原始ta
var listOfTasks = new List<Task>();
foreach (var file in files)
{
    var localFile = file;
    // Note that we create the Task here, but do not start it.
    listOfTasks.Add(new Task(async () => await blobBlock.UploadFromFileAsync(localFile, FileMode.Create)));
}
await Tasks.StartAndWaitAllThrottledAsync(listOfTasks, 5);