在无限循环中,Std::thread在随机时间后锁定

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

本文关键字:时间 锁定 随机 thread Std 无限循环      更新时间:2023-10-16

我试图实现3个额外的线程到我的主应用程序做非共享操作。

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

应用程序不会崩溃,只是应用程序的cpu使用率变为0%(以及每个线程)并且它没有响应。停止在visual studio调试器中显示行while(mWakeUp) mWaitCondition.Wait()在WaitThread()函数中作为当前位置。它还显示mWakeUp对所有线程都是false,所以它不应该停留在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()解除阻塞,循环条件才会被测试。

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

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

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