代码段,最多包含以FIFO顺序执行的N个线程

时间:2012-06-15 10:47:35

标签: c# multithreading algorithm parallel-processing synchronization

我有一段代码应该由低于N的最大线程数执行,并且线程调用someFunction()的顺序应该反映在它们进入该部分的顺序中,即FIFO订单。

如果我使用Semaphore,我无法控制线程进入该部分的顺序。

  

“没有保证订单,例如FIFO或LIFO,其中已被阻止   线程进入信号量。“

最初的尝试:

class someClass
{
    static volatile Semaphore semaphore;
    ...

    someClass()
    {
        semaphore = new Semaphore(N,N)
    }

    someType someFunction(InputType input)
    {
        try
        {
            semaphore.WaitOne();
            /* Section Begins */
             var response = someHeavyJob(input); // submitted to the server
            return response;
            /* Section Ends */
        }
        finally
        {
            semaphore.Release();
        } 
    }
}

如果我将Semaphore和ConcurrentQueue组合在一起,则以下线程可能会返回对其他线程带来的请求的响应,这需要对代码的其他部分进行重大更改。 什么是针对以下问题的.NET 4.5解决方案:

  1. 允许代码部分中的最大线程数小于N
  2. 线程进入该部分的顺序是FIFO
  3. 线程将获得他们带来的请求的响应(而不是其他线程对请求的响应)

    class someClass
    {
        static volatile ConcurrentQueue<someType> cqueue;
        static volatile Semaphore semaphore;
        ...
    
        someClass()
        {
           cqueue = new ConcurrentQueue<someType>();
           semaphore = new Semaphore(N,N)
        }
    
        someType someFunction(Request request)
        {
            try
            {
                cqueue.enqueue(request);
                semaphore.WaitOne();
                Request newrequest;
                cqueue.TryDequeue(out newrequest);
    
                /* Section Begins */
    
                var response = someHeavyJob(Request newrequest); // submitted to the server
                return response;
                /* Section Ends */
            }
            finally
            {
                semaphore.Release();
            }
        }
    }
    
  4. 更新: 我在澄清我的问题: SomeHeavyJobs()函数是对正在处理此作业的服务器的阻塞调用。

    UPDATE2: 谢谢大家的答案。为了记录:我最终使用了FIFO Semaphore

3 个答案:

答案 0 :(得分:1)

你看过Smart Thread Pool吗?

<强> [编辑]

如果我仍然正确地解决问题,正如我在评论中所说,我不相信多线程解决方案对于这个问题是可行的。

如果在 k-1 任务完成之前无法启动任务 k ,那么您只需要一个线程来执行它们。如果允许您并行执行某些任务组合,则需要准确指定规则。

答案 1 :(得分:1)

  

也应该是线程调用someFunction()的顺序   反映在他们进入该部分的顺序,即   FIFO订单

原则上这是不可能的。

semaphore.WaitOne(); //#1
var response = someHeavyJob(input); //#2

即使Semaphore是严格的FIFO,也可能发生以下情况:

  1. 所有主题以FIFO顺序(1)
  2. 进入该部分
  3. 所有线程都从CPU(1到2之间)进行了调度
  4. 所有主题以随机顺序重新安排,甚至以LIFO顺序(1到2之间)重新安排
  5. 所有线程开始以任意顺序(2)
  6. 输入someHeavyJob

    您永远无法确保线程将按特定顺序“输入”该功能。

    对于FIFO信号量,您可以使用锁和队列自己构建信号量。看起来你已经这样做并发布了代码。据我所知,这种方法是正确的。

答案 2 :(得分:1)

'如果我将Semaphore和ConcurrentQueue组合在一起,如下所示,线程可能会返回对其他线程提出的请求的响应,这需要对代码的其他部分进行重大更改。'

我讨厌这样说,但我会建议“代码的其他部分发生变化”,即使我不知道这会有多大“重要性”。

典型地,通过对包含对原始类实例的引用的消息进行排队,以便可以将响应“返回”到请求它们的对象,可以如您所建议的那样满足这样的要求。如果发起者都来自某个'messagehandler'类,那么这将使调用该函数的线程变得更容易(它应该是messagehandler的成员)。一旦线程执行了该函数,它们就可以调用messagehandler的'onCompletion'方法。 'onCompletion'可以发信号通知发起者正在等待的事件,(同步),或者将某些事件排队到发起者的私有P-C队列,(异步)。

因此,一个BlockingCollection,一个消费者线程和明智地使用C ++ / C#继承/多态应该可以完成这项工作。

奇怪的是,这几乎就是我当前嵌入式ARM项目所强加的内容。用于config / debug / log的命令行界面线程现在非常大,即使在“Thumb,Optimize of size”模式下,它也需要大量600字的堆栈。它不能再被允许直接调用SD文件系统,现在必须将自己排队到运行SD卡的线程(它在系统中具有运行FAT32的最大堆栈),并等待SD线程的信号量到调用它的方法并在完成后发出信号。

这是确保按顺序进行调用并确保工作的经典方法。它基本上是一个只有一个线程的线程池。

就像其他海报写的那样,任何其他方法都可能是,错误的......'勇敢'。