2

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

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

“没有保证的顺序,例如 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. 线程进入section的顺序是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();
            }
        }
    }
    

更新:我正在澄清我的问题:SomeHeavyJobs() 函数是对正在处理此作业的服务器的阻塞调用。

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

4

3 回答 3

1

你看过智能线程池吗?

[编辑]

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

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

于 2012-06-15T11:09:13.067 回答
1

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

这在原则上是不可能的。

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

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

  1. 所有线程按先进先出顺序进入区段(1)
  2. 所有线程都从 CPU 中取消调度(1 到 2 之间)
  3. 所有线程都以随机顺序甚至 LIFO 顺序(1 到 2 之间)重新调度
  4. 所有线程开始以任意顺序进入 someHeavyJob (2)

您永远无法确保线程会以特定顺序“进入”函数。

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

于 2012-06-15T11:29:56.917 回答
1

“如果我将 Semaphore 和 ConcurrentQueue 组合如下,线程可能会返回对其他线程带来的请求的响应,这将需要对代码的其他部分进行重大更改。”

我不想这么说,但我会建议“更改代码的其他部分”,即使我不知道这会有多大的“意义”。

通常,按照您的建议,通过排队包含对原始类实例的引用的消息来满足这样的要求,以便可以将响应“返回”给请求它们的对象。如果发起者都来自某个“消息处理程序”类,这使得调用该函数的线程更容易(它应该是消息处理程序的成员)。一旦线程执行了函数,它们就可以调用消息处理程序的“onCompletion”方法。“onCompletion”可以表示发起者正在等待的事件(同步),或者将某些内容排队到发起者的私有 PC 队列中(异步)。

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

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

这是确保呼叫按顺序进行并且始终有效的经典方式。它基本上是一个只有一个线程的线程池。

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

于 2012-06-15T12:29:03.247 回答