C# 将工作从多个线程分派到一个同步线程
假设我有10个线程在忙着做一些事情,它们有时会调用一个方法C# 将工作从多个线程分派到一个同步线程,c#,.net,multithreading,C#,.net,Multithreading,假设我有10个线程在忙着做一些事情,它们有时会调用一个方法 public HandleWidgets(Widget w) { HeavyLifting(w) } 但是,我不希望我的10个线程等待HeavyLifting(w),而是将HeavyLifting(w)工作分派给第11个线程HeavyLifter线程并异步继续。分派到的HeavyLifter线程应该始终是同一个线程,并且我不想创建多个线程(因此,我不能执行类似以下的操作:) HeavyLifting(w)是“fire and for
public HandleWidgets(Widget w) { HeavyLifting(w) }
但是,我不希望我的10个线程等待HeavyLifting(w),而是将HeavyLifting(w)工作分派给第11个线程HeavyLifter线程并异步继续。分派到的HeavyLifter线程应该始终是同一个线程,并且我不想创建多个线程(因此,我不能执行类似以下的操作:)
HeavyLifting(w)是“fire and forget”,因为调用HandleWidgets()的线程不需要回调或类似的东西
这方面的健康做法是什么?创建一个在所有线程之间共享的队列和一个在所有线程之间共享的信号量。工作线程(不应等待HeavyLifter)发布如下请求:
lock (queue)
{
queue.Enqueue(request);
semaphore.Release();
}
HeavyLifter是一个后台线程(不会停止进程退出),在无限循环中运行以下代码:
while (true)
{
semaphore.WaitOne();
Request item = null
lock (queue)
{
item = queue.Dequeue();
}
this.ProcessRequest(item);
}
编辑:输入错误。您可以创建一个有限并发
任务调度程序
,用作任务
工厂,如中所述,限制为单线程:
var lcts = new LimitedConcurrencyLevelTaskScheduler(1);
TaskFactory factory = new TaskFactory(lcts);
您可以通过以下方式实现您的功能:
public HandleWidgets(Widget w)
{
factory.StartNew(() => HeavyLifting(w));
}
---编辑---
我刚刚注意到您需要“开火并忘记”,在这种情况下,单是阻止收集就足够了。下面的解决方案适用于更复杂的场景,您需要返回结果、传播异常或以某种方式(例如通过异步/等待)组合任务等
用于将“同步”线程中完成的工作作为基于API的API公开给“客户端”线程 对于
HandleWidgets
(CT=“client-thread”,“ST”=同步线程)的每次调用:
- CT:创建一个单独的
TaskCompletionSource
- CT:Dispatch
到ST(可能通过a;还将HeavyLifting
传递给它,以便它可以执行下面的最后一步)TaskCompletionSource
- CT:将
返回给调用者,而不必等待ST上的工作完成TaskCompletionSource
- CT:正常继续。如果/当不等待
完成(在ST中)就无法继续时,请等待上述任务重型提升
- ST:HeavyLifting完成后,调用(或,视情况而定),这将取消阻止当前可能等待任务的任何CT
HeavyLifting
。它只需等待项目可用,然后进行处理:
调用Take可能会被阻止,直到可以删除某个项目
其他线程可以简单地向集合添加项
请注意,BlockingCollection
不保证自己添加/删除的项目的顺序:
删除项的顺序取决于用于创建BlockingCollection实例的集合类型。创建BlockingCollection对象时,可以指定要使用的集合类型
我很惊讶这里没有提到其他答案。将一组块连接在一起,并通过链发布数据。可以显式控制每个块的并发性,因此可以执行以下操作:
var transformBlk =
new TransformBlock<int,int>(async i => {
await Task.Delay(i);
return i * 10;
}, new ExecutionDataflowBlockOptions{MaxDegreeOfParallelism = 10});
var processorBlk=
new ActionBlock<int>(async i => {
Console.WriteLine(i);
},new ExecutionDataflowBlockOptions{MaxDegreeOfParallelism = 1});
transformBlk.LinkTo(processorBlk);
var data = Enumerable.Range(1, 20).Select(x => x * 1000);
foreach(var x in data)
{
transformBlk.Post(x);
}
var transformBlk=
新TransformBlock(异步i=>{
等待任务。延迟(i);
返回i*10;
},新的ExecutionDataflowBlockOptions{MaxDegreeOfParallelism=10});
var processorBlk=
新操作块(异步i=>{
控制台写入线(i);
},新的ExecutionDataflowBlockOptions{MaxDegreeOfParallelism=1});
transformBlk.LinkTo(processorBlk);
var数据=可枚举范围(1,20)。选择(x=>x*1000);
foreach(数据中的变量x)
{
变形金刚柱(x);
}
您的线程设置很奇怪。与其将“繁重的工作”分包到多个线程,不如将“繁重的工作”分包到多个线程,然后将一个线程与实际工作联系起来。当你试图从多个内核中获取收益时,这与你通常想要的正好相反。我已经编辑了你的标题。请看,“,其中的共识是“不,他们不应该”。嗨,德列夫,这是不寻常的。我的实际应用是,这十个线程与外部硬件接口,第十一个线程解决与从硬件整理数据有关的问题。我不应该说更多;)