一旦第一组工作线程完成处理,我如何使用 AutoResetEventHandler 向主线程函数发出信号以再次启动线程

Posted

技术标签:

【中文标题】一旦第一组工作线程完成处理,我如何使用 AutoResetEventHandler 向主线程函数发出信号以再次启动线程【英文标题】:How can i use AutoResetEventHandler to signal Main thread function to start threads again once the first set of worker threads are done processing 【发布时间】:2013-01-14 01:42:18 【问题描述】:

要求:- 在任何给定时间点,只有 4 个线程应该调用四个不同的函数。一旦这些线程完成,下一个可用线程应该调用相同的函数。

当前代码:- 这似乎是实现此类目标的最糟糕的方法。 While(True) 会导致不必要的 CPU 峰值,运行以下代码时我可以看到 CPU 上升到 70%。

问题:-我如何使用 AutoResetEventHandler 向主线程 Process() 函数发出信号,以便在前 4 个工作线程完成处理后再次启动下一个 4 个线程,而不会浪费 CPU 周期。请推荐

public class Demo

    object protect = new object();
    private int counter;
    public void Process()
    
        int maxthread = 4;
        while (true)
        
            if (counter <= maxthread)
            
                counter++;
                Thread t = new Thread(new ThreadStart(DoSomething));
                t.Start();
            
        
    
    private void DoSomething()
    
        try
        
            Thread.Sleep(50000); //simulate long running process
        
        finally
        
            lock (protect)
            
                counter--;
            
        
    

【问题讨论】:

您能告诉我们更多关于您为什么要这样做的信息吗?我问是因为 .Net 框架可能已经支持更好的解决方案。 我被要求分析此代码并提供更好的方法来完成此操作,以便防止 CPU 峰值。 【参考方案1】:

您可以使用TPL 以更简单的方式实现您想要的。如果您运行下面的代码,您会注意到每个线程终止后都会写入一个条目,并且只有在所有四个线程都终止后才会写入 "Finished batch" 条目。

此示例使用Task.WaitAll 等待所有任务完成。该代码使用无限循环仅用于说明目的,您应根据您的要求计算hasPendingWork 条件,以便仅在需要时启动新的一批任务。

例如:

private static void Main(string[] args)

    bool hasPendingWork = true;
    do
    
        var tasks = InitiateTasks();

        Task.WaitAll(tasks);

        Console.WriteLine("Finished batch...");
     while (hasPendingWork);


private static Task[] InitiateTasks()

    var tasks = new Task[4];

    for (int i = 0; i < tasks.Length; i++)
    
        int wait = 1000*i;

        tasks[i] = Task.Factory.StartNew(() =>
        
            Thread.Sleep(wait);
            Console.WriteLine("Finished waiting: 0", wait);
        );
    

    return tasks;

另一件事,从您问题的文本要求部分,我相信一批四个新线程应该只在之前的四个线程完成后才开始。但是,您发布的代码与该要求不兼容,因为它在前一个线程终止后立即启动一个新线程。你应该明确你的要求到底是什么。


更新:

如果您想在四个线程之一终止后立即启动一个线程,您仍然可以使用 TPL 而不是显式启动新线程,但您可以使用 SemaphoreSlim 将正在运行的线程数限制为四个。例如:

private static SemaphoreSlim TaskController = new SemaphoreSlim(4);

private static void Main(string[] args)

    var random = new Random(570);

    while (true)
    
        // Blocks thread without wasting CPU
        // if the number of resources (4) is exhausted
        TaskController.Wait();

        Task.Factory.StartNew(() =>
        
            Console.WriteLine("Started");
            Thread.Sleep(random.Next(1000, 3000));
            Console.WriteLine("Completed");
            // Releases a resource meaning TaskController.Wait will unblock
            TaskController.Release();
        );
    

【讨论】:

已更新要求。提供的代码正是需要做的。感谢您指出。

以上是关于一旦第一组工作线程完成处理,我如何使用 AutoResetEventHandler 向主线程函数发出信号以再次启动线程的主要内容,如果未能解决你的问题,请参考以下文章

只有在上一组工作完成后才循环执行一组工作

我如何分析多线程问题?

如何重用线程 - pthreads c

在递归方法中如何知道我的所有线程何时完成执行?

NIO 的工作方式

Servlet容器如何同时来处理多个请求