c# - 最多有 N 个线程按 FIFO 顺序执行的代码部分

标签 c# multithreading algorithm parallel-processing synchronization

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

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

"There is no guaranteed order, such as FIFO or LIFO, in which blocked threads enter the semaphore."

初步尝试:

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();
        } 
    }
}

如果我将信号量和并发队列组合起来,如下所示,线程可能会返回对其他线程带来的请求的响应,这将需要对代码的其他部分进行重大更改。 针对以下问题的 .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();
            }
        }
    }
    

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

更新2: 谢谢大家的解答。郑重声明:我最终使用了 FIFO Semaphore

最佳答案

“如果我将信号量和并发队列组合起来,如下所示,线程可能会返回对其他线程带来的请求的响应,这将需要对代码的其他部分进行重大更改。”

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

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

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

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

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

就像其他发帖者所写的那样,任何其他方法都可能是,呃……“勇敢”。

关于c# - 最多有 N 个线程按 FIFO 顺序执行的代码部分,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/11049221/

相关文章:

c# - If语句不能改变局部变量

python - 使用多 GPU 和多线程、Pytorch 进行对象检测推理

c# - 有没有一种简单的方法可以将序数数字字符串转换为其匹配的数值?

c# - 内存中的字符串对象总数

c# - 为什么 Resharper 认为这个 catch 子句是多余的?

java - 您应该同步对 Java 中的属性的访问吗?

c++ - 使用 std::thread & std::bind 在成员函数中启动线程

algorithm - 使用 Golang 的遗传算法中的轮盘赌选择

python - 这个mergesort实现的时间复杂度是O(nlogn)吗?

c# - 匿名类型到真实类重构