最多包含 N 个线程的代码部分,按 FIFO 顺序执行

本文关键字:FIFO 执行 顺序 代码部 包含 线程 | 更新日期: 2023-09-27 18:37:23

我有一段代码,应该由最大数量的线程执行,线程调用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();
        } 
    }
}

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

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

    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() funciton 是对正在处理此作业的服务器的阻塞调用。

UPDATE2:谢谢大家的回答。郑重声明:我最终使用了FIFO信号量

最多包含 N 个线程的代码部分,按 FIFO 顺序执行

"如果我像下面这样组合信号灯和 ConcurrentQueue,线程可能会返回对其他线程带来的请求的响应,这需要对代码的其他部分进行重大更改。

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

通常,如

您所建议的那样,通过将包含对原始类实例的引用的消息排队来满足,以便可以将响应"返回"到请求它们的对象。 如果发起方都是某个"消息处理程序"类的后代,那么在将调用该函数的线程(应该是消息处理程序的成员)上会更容易。 线程执行函数后,它们可以调用消息处理程序的"onCompletion"方法。"onCompletion"可以发出发起方正在等待的事件的信号(同步),也可以将某些内容排队到发起方的私有 P-C 队列(异步)。

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

奇怪的是,这几乎正是我目前的嵌入式ARM项目被迫进入的。用于配置/调试/日志的命令行界面线程现在非常大,以至于即使在"拇指,优化大小"模式下,它也需要大量的 600 字堆栈。不能再允许它直接调用 SD 文件系统,现在必须将自身排队到运行 SD 卡的线程(该线程具有系统中运行 FAT32 的最大堆栈),并在信号灯上等待 SD 线程调用其方法并在完成后发出信号量信号。

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

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

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

原则上这是不可能的。

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

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

  1. 所有线程按先进先出顺序进入该部分 (1)
  2. 所有线程都从 CPU 取消调度(介于 1 和 2 之间)
  3. 所有线程都以随机顺序甚至后进先出顺序(1 到 2 之间)重新调度
  4. 所有线程开始以任意顺序进入某个繁重作业 (2)

您永远无法确保线程将按特定顺序"进入"函数。

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

你看过智能线程池吗?

[编辑]

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

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