C# 如何限制c语言中并行任务的最大数量#
我有1000条输入消息要处理。我正在循环输入集合,并开始处理每条消息的新任务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
//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));
}
}