假设我有10个线程忙于做某事,他们有时会调用方法
public HandleWidgets(Widget w) { HeavyLifting(w) }
但是,我不希望我的10个线程在HeavyLifting(w)上等待,而是将HeavyLifting(w)工作分配给第11个线程,即HeavyLifter线程并异步继续。调度的HeavyLifter线程应该始终是同一个线程,我不想创建多个线程(因此,我不能做这样的事情:C# Asynchronous call without EndInvoke?)。
HeavyLifting(w)是“发射并忘记”,因为调用HandleWidgets()的线程不需要回调或类似的东西。
这是一种健康的做法?
答案 0 :(得分:2)
我很惊讶这里没有其他答案提到TPL DataFlow。您将一堆块连接在一起并通过链发布数据。您可以显式控制每个块的并发性,因此您可以执行以下操作:
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);
}
答案 1 :(得分:1)
创建一个在所有线程和一个信号量之间共享的队列,该信号量也在所有线程之间共享。工作线程(不应该等待HeavyLifter)发布这样的请求:
lock (queue)
{
queue.Enqueue(request);
semaphore.Release();
}
HeavyLifter是一个后台线程(不会停止进程退出)并在无限循环中运行以下代码:
while (true)
{
semaphore.WaitOne();
Request item = null
lock (queue)
{
item = queue.Dequeue();
}
this.ProcessRequest(item);
}
编辑:错字。
答案 2 :(得分:1)
您可以创建一个有限并发TaskScheduler
作为Task
工厂,如this example from MSDN中所提供,但对单线程有限制:
var lcts = new LimitedConcurrencyLevelTaskScheduler(1);
TaskFactory factory = new TaskFactory(lcts);
实现你的功能:
public HandleWidgets(Widget w)
{
factory.StartNew(() => HeavyLifting(w));
}
答案 3 :(得分:1)
---编辑---
我只是注意到你需要“开火并忘记”,在这种情况下,单独阻挡收集就足够了。下面的解决方案实际上适用于需要返回结果,传播异常或以某种方式组合任务(例如通过异步/等待)等更复杂的场景......
使用TaskCompletionSource
将“同步”线程中完成的工作作为基于Task
的API公开给“客户端”线程。
每次调用HandleWidgets
(CT =“客户端线程”,“ST”=同步线程):
TaskCompletionSource
。HeavyLifting
(可能通过BlockingCollection
;也可以将TaskCompletionSource
传递给它,因此可以执行下面的最后一步。)TaskCompletionSource
的{{3}}返回给来电者,无需等待ST上的工作完成。HeavyLifting
完成(在ST中)时无法再继续,请等待上面的任务。HeavyLifting
完成后,请拨打Task
(或SetResult
或SetException
,视情况而定),取消阻止当前可能等待任务的所有CT。< / LI>
答案 4 :(得分:1)
基本上你有 producer 工作的线程和一个使用者的线程。
创建一个帖子并在循环中从Take
获取BlockingCollection
。这是您的使用者主题,它将调用HeavyLifting
。它只会等到一个项目可用,然后处理它:
对Take的调用可能会阻止,直到某个项目可以删除。
其他线程可以简单地将项目添加到集合中。
请注意,BlockingCollection
并不保证自行添加/删除项目的排序:
删除项目的顺序取决于用于创建BlockingCollection实例的集合类型。创建BlockingCollection对象时,可以指定要使用的集合类型