从多个线程到一个同步线程的调度工作

时间:2013-12-20 23:27:31

标签: c# .net multithreading

假设我有10个线程忙于做某事,他们有时会调用方法

public HandleWidgets(Widget w) {  HeavyLifting(w) }

但是,我不希望我的10个线程在HeavyLifting(w)上等待,而是将HeavyLifting(w)工作分配给第11个线程,即HeavyLifter线程并异步继续。调度的HeavyLifter线程应该始终是同一个线程,我不想创建多个线程(因此,我不能做这样的事情:C# Asynchronous call without EndInvoke?)。

HeavyLifting(w)是“发射并忘记”,因为调用HandleWidgets()的线程不需要回调或类似的东西。

这是一种健康的做法?

5 个答案:

答案 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”=同步线程):

  • CT:创建单独的TaskCompletionSource
  • CT:向ST发送HeavyLifting(可能通过BlockingCollection;也可以将TaskCompletionSource传递给它,因此可以执行下面的最后一步。)
  • CT:将TaskCompletionSource的{​​{3}}返回给来电者,无需等待ST上的工作完成。
  • CT:继续正常。如果/当不再等待HeavyLifting完成(在ST中)时无法再继续,请等待上面的任务。
  • ST:HeavyLifting完成后,请拨打Task(或SetResultSetException,视情况而定),取消阻止当前可能等待任务的所有CT。< / LI>

答案 4 :(得分:1)

基本上你有 producer 工作的线程和一个使用者的线程。

创建一个帖子并在循环中从Take获取BlockingCollection。这是您的使用者主题,它将调用HeavyLifting。它只会等到一个项目可用,然后处理它:

  

对Take的调用可能会阻止,直到某个项目可以删除。

其他线程可以简单地将项目添加到集合中。

请注意,BlockingCollection并不保证自行添加/删除项目的排序:

  

删除项目的顺序取决于用于创建BlockingCollection实例的集合类型。创建BlockingCollection对象时,可以指定要使用的集合类型