C + + thread class destruct deadlock problem

1, Foreword

  • This paper will introduce a case that the main thread actively delete s the sub thread object, resulting in the main thread jamming (infinite waiting);

2, Cited examples

1. Case description

  • 1) The implementation includes a main thread and a sub thread;
  • 2) The main thread is responsible for starting the child thread and destroying it after the child thread runs for 500ms:
  • 3) The sub thread outputs a keep alive message every 16ms;

2. Case realization

#include "Thread.h"


class LogicThread : public Thread
{
public:
	LogicThread() : m_bStop(false) {
	}
protected:
	virtual void DoWork() {
		while (!m_bStop) {
			printf("Thread[%d]: I'm active!\n", GetId());
			Sleep(16);
		}
		printf("Thread[%d]: I'm exit!\n", GetId());
	}

private:
	bool                      m_bStop;
};

int main() {
	LogicThread *pLS = new LogicThread;
	pLS->Start();
	Sleep(500);
	delete pLS;
	return 0;
}
  • Can the delete of this code be executed smoothly? The answer is no!

3, Problem analysis

1, Output

  • First, run and look at the console output. The thread will output I'm active! In an infinite loop:
Thread[70508]: I'm active!
Thread[70508]: I'm active!
Thread[70508]: I'm active!
Thread[70508]: I'm active!
Thread[70508]: I'm active!
Thread[70508]: I'm active!
...

2, Analysis

  • The breakthrough of analysis lies in the call of delete;
  • If there is no delete here, the main thread can successfully go to return 0;
  • However, after delete is added, the destructor of the base class Thread will be called to call the Stop() interface of the sub Thread. The implementation is as follows:
Thread::~Thread() {
	Stop();
}

void Thread::Stop() {
	if (!m_pkHandle) {
		return;
	}
	WaitForSingleObject((HANDLE)m_pkHandle, INFINITE);
	CloseHandle((HANDLE)m_pkHandle);
	m_pkHandle = NULL;
}
  • Since the sub thread has been running without returning, WaitForSingleObject will block and wait all the time. This call is made in the main thread, so the phenomenon is that the main thread is stuck;

4, Improvement scheme

  • In order to prevent WaitForSingleObject from waiting indefinitely, it is necessary to ensure that the logical thread can be destroyed only after the thread function exits;
  • Therefore, you can poll the sub thread state in the main thread, and the exit logic can be executed only when the sub thread enters the exit state;
  • In order to describe the problem clearly, a thread end condition is added here. When the counter 100 counts, m will be added_ Bstop set to true to stop the thread;
#include "Thread.h"


class LogicThread : public Thread
{
public:
	LogicThread() : m_bStop(false) {
	}
protected:
	virtual void DoWork() {
		int nCnt = 100;
		while (!m_bStop) {
			printf("Thread[%d]: I'm active!\n", GetId());
			Sleep(16);
			if (--nCnt == 0) {
				m_bStop = true;  // Add end condition
			}
		}
		printf("Thread[%d]: I'm exit!\n", GetId());
	}
private:
	bool                      m_bStop;
};

int main() {
	LogicThread *pLS = new LogicThread;
	pLS->Start();
	// The main thread waits for the child thread to exit;
	do {
		Sleep(16);
	} while (pLS->IsRunning());
	// Clean up thread information
	delete pLS;
	return 0;
}

Tags: C++ Multithreading

Posted by asy1mpo on Fri, 13 May 2022 15:36:10 +0300