首页 > 解决方案 > 在多线程问题中没有得到预期的输出

问题描述

任务描述:

编写一个程序,读取一个正整数值n ( n > 3),然后创建n 个线程(每个线程都有id;id从 1 开始)并一直工作直到它收到一个停止信号。所有n 个线程都在等待信号。每隔一个主线程发送一个随机线程的信号,然后该线程应该打印它id并返回到等待状态。

要求:

所有额外的线程都应该正确完成。在线程函数退出时,应打印有关退出的消息。在线程等待条件变量时,应检查虚假唤醒。只std::cout允许文本输出。停止信号是 SIGINT (ctrl+c)

我为上述问题编写了以下代码,但在输出中,所有线程都没有退出。我无法弄清楚这个问题,因为我是这个话题的新手。任何形式的帮助将不胜感激。

class Program
{
    public static void Main()
    {
        var numberofthreads = Convert.ToInt32(Console.ReadLine());
        ProcessingClass myobject = new ProcessingClass();
        myobject.createThreads(numberofthreads);
    }
}

public class ProcessingClass
{
    public Mutex mymutex = new Mutex();

    private bool thread_flag = false;

    public void createThreads(int numberofthreads)
    {
        var threads = new List<Thread>(numberofthreads);
        for (int i = 0; i < numberofthreads; i++)
        {
            Thread th = new Thread(() =>
            {
                threadsworking();
            });

            th.Name = "Thread" + i;
            th.Start(); // <-- .Start() makes the thread start running   
            threads.Add(th);
        }

        Console.CancelKeyPress += (object sender, ConsoleCancelEventArgs e) =>
        {
            var isCtrlC = e.SpecialKey == ConsoleSpecialKey.ControlC;
            if (isCtrlC)
            {
                thread_flag = true;
                int num = 1;
                foreach (var thread in threads)
                {
                    thread.Join();
                    Console.WriteLine($"Thread {num} exits");
                    num++;
                }

            }
            e.Cancel = true;
        };

    }

    public void threadsworking()
    {
        while (thread_flag == false)
        {
            mymutex.WaitOne();   // Wait until it is safe to enter.  
            Console.WriteLine("{0}", Thread.CurrentThread.Name);
            Thread.Sleep(1000);    // Wait until it is safe to enter. 
            mymutex.ReleaseMutex();    // Release the Mutex.  
        }
    }
}

在此处输入图像描述

标签: c#multithreading

解决方案


考虑防止mutex阻塞线程退出。

当您使用mutex.WaitOne()它时,它会阻止执行,直到Mutex它归该线程所有。这对于确保线程对共享资源具有独占控制权非常有帮助。但是,当您想要任意结束这些线程时,例如当您在Console.CancelKeyPress.

thread.Join()您可以通过在事件中调用之前和之后记录来查看此效果。

thread_flag = true;
int num = 1;
foreach (var thread in threads)
{
    Console.WriteLine($"Joining {thread.Name}");
    
    thread.Join();
    
    Console.WriteLine($"Joined {thread.Name}");
    
    Console.WriteLine($"Thread {num} exits");

    num++;
}

当我们执行该日志记录时,它会告诉我们,当您调用Join()Thread #1 时,您会看到Joining 1. 然后有一个长的停顿,其他线程仍在工作,最后所有线程都背靠背加入。

这样做的原因是 - 在Join()等待线程 1 完成时,线程 1 仍在等待互斥锁。

即使您将thread_flag标志设置为true,线程 1 也无法退出,因为它没有获得互斥锁的所有权来执行它的工作并最终退出while()循环。

我们可以相当简单地解决这个问题。

在等待互斥体时考虑使用超时
当您调用.WaitOne(n)时,mutex您可以等待给n定的毫秒数并放弃对互斥体的所有权。

这将允许对循环进行更频繁的评估while,随后该threadsworking方法检查是否应该退出(使用thread_flag标志)的次数更多。

这是一个简短的示例,如何实现该更改

public void threadsworking()
{
    while (thread_flag == false)
    {
        // wait to enter the mutex, give timeout to prevent blocking 
        // until mutex opens and use the bool returned to determine
        // if we should release the mutex or not
        if (mymutex.WaitOne(1))
        {
            try
            {
                Console.WriteLine("{0}", Thread.CurrentThread.Name);
                Thread.Sleep(1000);    // Wait until it is safe to enter. 
            }
            finally
            {
                // make sure even if we encounter an error the mutex is released
                mymutex.ReleaseMutex();    // Release the Mutex.
            }
        }

        // allow other threads to continue their work instead of blocking with a while loop, this is optional depending on the workload
        Thread.Yield();
    }
}

推荐阅读