std::thread 在无限循环中的随机时间后锁定

std::thread locks after a random time in an endless loop

我正在尝试为执行非共享操作的主应用程序实施 3 个额外的线程。

起初我认为它可以工作,因为如果我取消注释 WorkerThread 函数中的最后一个 printf 调用,它不会在随机时间后锁定在 WaitThread()。如果没有 printf,有时需要几秒钟才能锁定 mWaitCond.Wait() 函数,有时刚开始就锁定。 printf 似乎修复了线程的时间。

应用程序没有崩溃,只是 cpu 应用程序的使用率变为 0%(和每个线程的使用率)并且没有响应。在 visual studio 调试器中停止显示 WaitThread() 函数中的行 while(mWakeUp) mWaitCondition.Wait() 作为当前位置。它还表明 mWakeUp 对于所有线程都是假的,所以它不应该留在那个 while 循环中。

我的设计理念:

  1. 在进入主无限循环之前调用 SetupThreads()
  2. 死循环中调用WorkerInit()唤醒线程
  3. 在我访问3个线程的数据之前,WorkerWait()被调用等待它们完成
  4. 在 WorkerThread 函数(由每个线程调用)中,我锁定互斥锁并等待线程被唤醒或停止
  5. 处理数据后,wakeUp 设置为 false 并且 condition_variable 通知

会不会是 waitthread 一个接一个地等待线程,当它等待让我们说索引 0 处的线程时,索引 2 处的线程继续 运行?

    static const ui32 NumContexts = 3;

    // array of pointers to threads
    std::thread* mThreadHandles[NumContexts];

    // wakup
    std::atomic<bool> mWakeUp[NumContexts];
    std::mutex mWakeMutex[NumContexts];
    std::condition_variable mWakeCondition[NumContexts];

    // wait for thread to finish task
    std::mutex mWaitMutex[NumContexts];
    std::condition_variable mWaitCondition[NumContexts];

    // stop signal
    std::atomic<bool> mStop[NumContexts];

    void Framework::SetupThreading()
    {
        // create and start threads
        for (int i = 0; i < NumContexts; i++)
        {
            this->mWakeUp[i] = false;
            this->mStop[i] = false;
            this->mThreadHandles[i] = new  std::thread(&Framework::WorkerThread, this, reinterpret_cast<void*>(i));
        }
    }

    //---------------------------------------------
    void Framework::WakeUpThread(int i)
    {
        {
            //auto lock = std::unique_lock<std::mutex>(this->mWakeMutex[i]);
            std::lock_guard<std::mutex> lock(this->mWakeMutex[i]);
            //printf("Waking up thread %i \n", i);

            this->mWakeUp[i] = true;
        }
        this->mWakeCondition[i].notify_one();
    }

    // THIS FUNCTION LOCKS
    //---------------------------------------------
    void Framework::WaitThread(int i)
    {
        auto lock = std::unique_lock<std::mutex>(this->mWaitMutex[i]);
        //printf("Waiting for thread %i to finish \n", i);

        while (this->mWakeUp[i])
            this->mWaitCondition[i].wait(lock);

        //printf("Thread %i finished! \n", i);
    }

    //---------------------------------------------
    void Framework::StopThread(int i)
    {
        auto lock = std::unique_lock<std::mutex>(this->mWakeMutex[i]);
        printf("Sending stop signal for thread %i \n", i);
        this->mStop[i] = true;
        this->mWakeCondition[i].notify_one();
    }

    //---------------------------------------------
    void Framework::JoinThread(int i)
    {
        printf("Waiting for join of thread %i \n", i);
        this->mThreadHandles[i]->join();
        printf("Thread %i joined! \n", i);
    }

    // THESE ARE CALLED IN THE MAIN LOOP
    //---------------------------------------------
    void Framework::WorkerInit()
    {
        for (int i = 0; i < NumContexts; i++)
        {
            this->WakeUpThread(i);
        }
    }

    void Framework::WorkerWait()
    {
        for (int i = 0; i < NumContexts; i++)
        {
            this->WaitThread(i);
        }
    }

    // THE FUNCTION CALLED BY THE THREADS
    //---------------------------------------------
    void Framework::WorkerThread(LPVOID workerIndex)
    {
        int threadIndex = reinterpret_cast<int>(workerIndex);
        while (threadIndex < NumContexts && threadIndex >= 0)
        {
            {
                auto lock = std::unique_lock<std::mutex>(this->mWakeMutex[threadIndex]);
                //printf("thread %i: waiting for wakeup or stop signal...\n", threadIndex);

                // not stopped nor woken up? continue to wait
                while (this->mWakeUp[threadIndex] == false && this->mStop[threadIndex] == false)
                {
                    this->mWakeCondition[threadIndex].wait(lock);
                }

                // stop signal sent?
                if (this->mStop[threadIndex])
                {
                    //printf("thread %i: got stop signal!\n", threadIndex);
                    return;
                }
                //printf("thread %i: got wakeup signal!\n", threadIndex);

                // lock unlocks here (lock destructor)
            }

            //  printf("thread %i: running the task...\n", threadIndex);

             // RUN CODE HERE

                //printf("thread %i finished! Sending signal!...\n", threadIndex);

                // m_wakeup is atomic so there is no concurrency issue with wait()
                this->mWakeUp[threadIndex] = false;
                this->mWaitCondition[threadIndex].notify_all();


        }
    }

如果线程的 CPU 使用为零,则它不会在 while 循环中旋转,而是在 wait() 上阻塞。在 wait() 解除阻塞之前,不会测试循环条件。

检查调试器中的调用堆栈以验证,暂停位置可能只是指示 您的 源代码中的 return 位置,而不是当前位置。

还要检查 WorkerThread 实例的状态 - 它们是否 运行 并正在调用 notify_all()?您的调试器线程知道吗?

我不确定我是否理解你的设计或意图,但从表面上看,它对我来说似乎有点过于复杂,并且已经成熟到陷入僵局的情况。