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 循环中。
我的设计理念:
- 在进入主无限循环之前调用 SetupThreads()
- 死循环中调用WorkerInit()唤醒线程
- 在我访问3个线程的数据之前,WorkerWait()被调用等待它们完成
- 在 WorkerThread 函数(由每个线程调用)中,我锁定互斥锁并等待线程被唤醒或停止
- 处理数据后,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()
?您的调试器线程知道吗?
我不确定我是否理解你的设计或意图,但从表面上看,它对我来说似乎有点过于复杂,并且已经成熟到陷入僵局的情况。
我正在尝试为执行非共享操作的主应用程序实施 3 个额外的线程。
起初我认为它可以工作,因为如果我取消注释 WorkerThread 函数中的最后一个 printf 调用,它不会在随机时间后锁定在 WaitThread()。如果没有 printf,有时需要几秒钟才能锁定 mWaitCond.Wait() 函数,有时刚开始就锁定。 printf 似乎修复了线程的时间。
应用程序没有崩溃,只是 cpu 应用程序的使用率变为 0%(和每个线程的使用率)并且没有响应。在 visual studio 调试器中停止显示 WaitThread() 函数中的行 while(mWakeUp) mWaitCondition.Wait() 作为当前位置。它还表明 mWakeUp 对于所有线程都是假的,所以它不应该留在那个 while 循环中。
我的设计理念:
- 在进入主无限循环之前调用 SetupThreads()
- 死循环中调用WorkerInit()唤醒线程
- 在我访问3个线程的数据之前,WorkerWait()被调用等待它们完成
- 在 WorkerThread 函数(由每个线程调用)中,我锁定互斥锁并等待线程被唤醒或停止
- 处理数据后,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()
?您的调试器线程知道吗?
我不确定我是否理解你的设计或意图,但从表面上看,它对我来说似乎有点过于复杂,并且已经成熟到陷入僵局的情况。