C# 如何限制c语言中并行任务的最大数量#

C# 如何限制c语言中并行任务的最大数量#,c#,.net,asynchronous,C#,.net,Asynchronous,我有1000条输入消息要处理。我正在循环输入集合,并开始处理每条消息的新任务 //Assume this messages collection contains 1000 items var messages = new List<string>(); foreach (var msg in messages) { Task.Factory.StartNew(() => { Process(msg); }); } //假设此邮件集合包含1000

我有1000条输入消息要处理。我正在循环输入集合,并开始处理每条消息的新任务

//Assume this messages collection contains 1000 items
var messages = new List<string>();

foreach (var msg in messages)
{
   Task.Factory.StartNew(() =>
   {
    Process(msg);
   });
 }
//假设此邮件集合包含1000项
var messages=新列表();
foreach(消息中的var msg)
{
Task.Factory.StartNew(()=>
{
工艺(味精);
});
}
我们可以猜测一次最多同时处理多少条消息(假设使用普通四核处理器),或者我们可以限制一次要处理的最大消息数吗


如何确保以集合的相同顺序/顺序处理此消息?

您可以创建自己的TaskScheduler并在那里覆盖QueueTask

protected virtual void QueueTask(Task task)
然后你可以做任何你喜欢的事

这里有一个例子:


您可以这样简单地设置最大并发度:

int maxConcurrency=10;
var messages = new List<1000>();
using(SemaphoreSlim concurrencySemaphore = new SemaphoreSlim(maxConcurrency))
{
    foreach(var msg in messages)
    {
        Task.Factory.StartNew(() =>
        {
            concurrencySemaphore.Wait();
            try
            {
                 Process(msg);
            }
            finally
            {
                concurrencySemaphore.Release();
            }
        });
    }
}
int-maxConcurrency=10;
var messages=新列表();
使用(SemaphoreSlim concurrency semaphore=new SemaphoreSlim(maxConcurrency))
{
foreach(消息中的var msg)
{
Task.Factory.StartNew(()=>
{
concurrency-ysemaphore.Wait();
尝试
{
工艺(味精);
}
最后
{
并发Maphore.Release();
}
});
}
}
您可以使用并依赖它


我认为最好使用并行LINQ

  Parallel.ForEach(messages ,
     new ParallelOptions{MaxDegreeOfParallelism = 4},
            x => Process(x);
        );

其中x是MaxDegreeOfParallelism

信号量Lim在这种情况下是一个非常好的解决方案,我强烈建议OP尝试一下,但是@Manoj的答案有评论中提到的缺陷。信号量应该在生成这样的任务之前等待

更新的答案:正如@Vasyl指出的,信号量可能在任务完成之前被释放,并且在调用
Release()
方法时会引发异常,因此在退出使用块之前必须等待所有创建的任务完成

int maxConcurrency=10;
var messages = new List<string>();
using(SemaphoreSlim concurrencySemaphore = new SemaphoreSlim(maxConcurrency))
{
    List<Task> tasks = new List<Task>();
    foreach(var msg in messages)
    {
        concurrencySemaphore.Wait();

        var t = Task.Factory.StartNew(() =>
        {
            try
            {
                 Process(msg);
            }
            finally
            {
                concurrencySemaphore.Release();
            }
        });

        tasks.Add(t);
    }

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

如果您需要按顺序排队(处理可能以任何顺序完成),则不需要信号量。老式的if语句很好用:

        const int maxConcurrency = 5;
        List<Task> tasks = new List<Task>();
        foreach (var arg in args)
        {
            var t = Task.Run(() => { Process(arg); } );

            tasks.Add(t);

            if(tasks.Count >= maxConcurrency)
                Task.WaitAny(tasks.ToArray());
        }

        Task.WaitAll(tasks.ToArray());
const int maxConcurrency=5;
列表任务=新列表();
foreach(args中的变量arg)
{
var t=Task.Run(()=>{Process(arg);});
任务。添加(t);
如果(tasks.Count>=maxConcurrency)
Task.WaitAny(tasks.ToArray());
}
Task.WaitAll(tasks.ToArray());
公共静态无效运行任务(列表导入任务列表)
{
List runningTasks=new List();
尝试
{
foreach(导入任务列表中的NamedTask currentTask)
{
currentTask.Start();
runningTasks.Add(当前任务);
if(runningTasks.Where(x=>x.Status==TaskStatus.Running).Count()>=MaxCountImportThread)
{
Task.WaitAny(runningTasks.ToArray());
}
}
Task.WaitAll(runningTasks.ToArray());
}
捕获(例外情况除外)
{
Log.Fatal(“错误!”,例如);
}
}
您可以使用,如果已达到消费收集限制,则产品将停止生产,直到消费过程结束。我发现这个模式比
信号量lim
更容易理解和实现

int TasksLimit = 10;
BlockingCollection<Task> tasks = new BlockingCollection<Task>(new ConcurrentBag<Task>(), TasksLimit);

void ProduceAndConsume()
{
    var producer = Task.Factory.StartNew(RunProducer);
    var consumer = Task.Factory.StartNew(RunConsumer);

    try
    {
        Task.WaitAll(new[] { producer, consumer });
    }
    catch (AggregateException ae) { }
}

void RunConsumer()
{
    foreach (var task in tasks.GetConsumingEnumerable())
    {
        task.Start();
    }
}

void RunProducer()
{
    for (int i = 0; i < 1000; i++)
    {
        tasks.Add(new Task(() => Thread.Sleep(1000), TaskCreationOptions.AttachedToParent));
    }
}
int TasksLimit=10;
BlockingCollection任务=新建BlockingCollection(新建ConcurrentBag(),TasksLimit);
无效生产和消费()
{
var producer=Task.Factory.StartNew(RunProducer);
var consumer=Task.Factory.StartNew(RunConsumer);
尝试
{
Task.WaitAll(新[]{生产者,消费者});
}
捕获(聚合异常ae){}
}
void RunConsumer()
{
foreach(tasks.getconsumineGenumerable()中的var task)
{
task.Start();
}
}
void RunProducer()
{
对于(int i=0;i<1000;i++)
{
添加(新任务(()=>Thread.Sleep(1000),TaskCreationOptions.AttachedToParent));
}
}

请注意,
RunProducer
RunConsumer
产生了两个独立的任务。

如果
进程
方法是异步的,则不能使用
任务.Factory.StartNew,因为它不能很好地处理异步委托。此外,在使用它时还有一些其他细微差别(例如,请参见)

在这种情况下,正确的方法是使用
Task.Run
。下面是为异步进程方法修改的@ClearLogic answer

static void Main(字符串[]args)
{
int-maxConcurrency=5;
List messages=Enumerable.Range(1,15)。选择(e=>e.ToString()).ToList();
使用(SemaphoreSlim concurrency semaphore=new SemaphoreSlim(maxConcurrency))
{
列表任务=新列表();
foreach(消息中的var msg)
{
concurrency-ysemaphore.Wait();
var t=Task.Run(异步()=>
{
尝试
{
等待过程(msg);
}
最后
{
并发Maphore.Release();
}
});
任务。添加(t);
}
Task.WaitAll(tasks.ToArray());
}
Console.WriteLine(“使用块退出”);
Console.ReadKey();
}
专用静态异步任务进程(字符串消息)
{            
等待任务。延迟(2000);
控制台写入线(msg);
}

如何将消息拆分为多个批并并行运行每个批?针对异步工作负载的相关问题:。这正是
并行的处理方式。ForEach
就是为这种处理而设计的。由于任务并行库是基于
线程池构建的,我们可以假设它只运行相同数量的线程池如果我们不明确指定任务,系统就有核心。这能确保消息的处理顺序与列表中出现的顺序相同吗?我不认为bit是在谈论完成,而是在谈论处理ord
        const int maxConcurrency = 5;
        List<Task> tasks = new List<Task>();
        foreach (var arg in args)
        {
            var t = Task.Run(() => { Process(arg); } );

            tasks.Add(t);

            if(tasks.Count >= maxConcurrency)
                Task.WaitAny(tasks.ToArray());
        }

        Task.WaitAll(tasks.ToArray());
 public static void RunTasks(List<NamedTask> importTaskList)
    {
        List<NamedTask> runningTasks = new List<NamedTask>();

        try
        {
            foreach (NamedTask currentTask in importTaskList)
            {
                currentTask.Start();
                runningTasks.Add(currentTask);

                if (runningTasks.Where(x => x.Status == TaskStatus.Running).Count() >= MaxCountImportThread)
                {
                    Task.WaitAny(runningTasks.ToArray());
                }
            }

            Task.WaitAll(runningTasks.ToArray());
        }
        catch (Exception ex)
        {
            Log.Fatal("ERROR!", ex);
        }
    }
int TasksLimit = 10;
BlockingCollection<Task> tasks = new BlockingCollection<Task>(new ConcurrentBag<Task>(), TasksLimit);

void ProduceAndConsume()
{
    var producer = Task.Factory.StartNew(RunProducer);
    var consumer = Task.Factory.StartNew(RunConsumer);

    try
    {
        Task.WaitAll(new[] { producer, consumer });
    }
    catch (AggregateException ae) { }
}

void RunConsumer()
{
    foreach (var task in tasks.GetConsumingEnumerable())
    {
        task.Start();
    }
}

void RunProducer()
{
    for (int i = 0; i < 1000; i++)
    {
        tasks.Add(new Task(() => Thread.Sleep(1000), TaskCreationOptions.AttachedToParent));
    }
}