多线程: 在自然死亡中发生死锁! 万分郁闷ing... 请高人帮忙!

风里有梦 2008-02-28 01:11:28
我写了一个多线程管理类, 如下:

typedef bool (*PFNTHREADPROC)(void*);

typedef enum enumThreadStatus :int32
{
tsNull = 0,
tsTerminated,
tsExiting,
tsPause,
tsRunning
}ThreadStatusEnum;

class CWorker
{
public:
...
bool SetJob( PFNTHREADPROC pfnTheadProc, void* pvParam,... )
{
...
m_pfnThreadProc = pfnTheadProc;
m_pvParam = pvParam;

m_hThread = (HANDLE)chBEGINTHREADEX( NULL, 0, CWorker::_ThreadProc, this, CREATE_SUSPENDED, &m_uThreadId );
...
}

void EndJob( bool bWaitExit )
{
...
_SetStatus( tsExiting );

if ( bWaitExit )
::WaitForSingleObject( m_hThread, INFINITE );

_SafeObjFree(::CloseHandle, m_hThread)
}
private:

static unsigned __stdcall _ThreadProc( void* pvParam );

PFNTHREADPROC m_pfnThreadProc;
void* m_pvParam;
...
}

unsigned __stdcall CWorker::_ThreadProc( void* pvParam )
{
CWorker* pWorker = (CWorker*) pvParam;

if ( pWorker->GetStatus() != tsRunning )
goto ExitFlags;

PFNTHREADPROC pfnThreadProc = pWorker->GetThreadProc();

if ( pfnThreadProc == NULL )
goto ExitFlags;

DWORD dwPrev = ::timeGetTime() + pWorker->GetFrequency();
while ( pWorker->GetStatus() > tsExiting )
{
if ( pWorker->IsRunning() && dwPrev < ::timeGetTime() )
{
(*pfnThreadProc) ( (void*)pWorker->GetThreadParam() );
dwPrev = ::timeGetTime() + pWorker->GetFrequency();
}
pWorker->Sleep( 30 );
}

ExitFlags:
pWorker->_SetStatus( tsTerminated );
_endthreadex( 0 ); // 出错点!

return 0;
}

这个类,我在使用一个线程时没事, 当我创建了多个后, 特别是100左右时, 就出现了在结束线程时死锁的问题!

以下调用代码:

线程定义:

#define MAX_WORKER 100
CWorker worker[MAX_WORKER];

线程函数:
bool Test( void* pvParam )
{
...
}

线程创建代码:
for ( int i = 0; i < MAX_WORKER; ++i )
worker[i].SetJob( test, (void*)i,... );

线程结束代码:
for ( int i = 0; i < MAX_WORKER; ++i )
worker[i].EndJob( TRUE );

当我在结束线程时告诉结束方法需要等线程完成退出后也就是收到信号后, 才做析构CWorker类的操作. 也就是这段代码:

if ( bWaitExit )
::WaitForSingleObject( m_hThread, INFINITE );

_SafeObjFree(::CloseHandle, m_hThread)

因为静态方法的线程在执行_endthreadex(0)时出现死锁,所以导致我的整个程序当掉! 请高人指点. 多谢!
...全文
471 45 打赏 收藏 转发到动态 举报
写回复
用AI写文章
45 条回复
切换为时间正序
请发表友善的回复…
发表回复
zh1369 2008-02-29
  • 打赏
  • 举报
回复
_endthreadex( 0 ); // 出错点!
可以不加直接返回即可. 好像windows核心编成讲过这个问题。
------
hityct1 2008-02-29
  • 打赏
  • 举报
回复
pfnThreadProc似乎指向
bool Test( void* pvParam )

这个函数干嘛用的?
hityct1 2008-02-29
  • 打赏
  • 举报
回复
CWorker::_ThreadProc( void* pvParam )
中第一个while循环是怎么回事,有可能跳不出循环吧



while ( pWorker-> GetStatus() > tsExiting )
{
if ( pWorker-> IsRunning() )
{
//pWorker-> m_pCSLock-> Enter();
(*pfnThreadProc) ( (void*)pWorker-> GetThreadParam() );
//pWorker-> m_pCSLock-> Leave();
}
pWorker-> Sleep( 30 );
}
}
风里有梦 2008-02-29
  • 打赏
  • 举报
回复
CSDN啊! 难道已到了没有能人的时代了吗? 汗!!!

风里有梦 2008-02-29
  • 打赏
  • 举报
回复
那个if就已经判断了, 下面的resume还用得着判断吗? 因为我的_beginthreadex就是用CREATE_SUSPENDED传入的, 所以说永远都是pause的状态, resume绝对不会出问题的, 更何况我都一一测试过, 每一个线程都运行的很好!

唯一的问题, 就是退出时....
hityct1 2008-02-29
  • 打赏
  • 举报
回复
另外CWorker::_SetJob 中
if( tsPause != GetStatus() )
{
::ResumeThread(m_hThread); //这判断一下是否成功会更好,
否则肯能会有问题。
_SetStatus( tsRunning );
}
michney 2008-02-29
  • 打赏
  • 举报
回复
代码够长的,有空帮你瞧瞧
风里有梦 2008-02-29
  • 打赏
  • 举报
回复
楼上的, 无论如何, 线程一直不能退出, 也是有问题, 我就算是这样等待, 逻辑上也是没有错误的, 虽然你说的那个更高效.
hityct1 2008-02-29
  • 打赏
  • 举报
回复
问题在
for ( int i = 0; i < MAX_WORKER; ++i )
worker[i].EndJob( TRUE );
你的每个EndJob都调用WaitForSingleObject
那岂不是假定这些线程必须依次结束吗?
如果第一个线程不结束,这就不会执行到第二个WaitForSingleObject。
应该用WaitforMultipleObjects。

你这应该是过度封装。
jasonshark 2008-02-29
  • 打赏
  • 举报
回复
调试,这个问题不好说,看你等待的线程死在哪里了,死在kernel里了也说不定.
我就遇到过因为网卡驱动的问题导致kernel锁住线程 CancelIO 300秒,在usermode下是看不着这个线程的. 你可以先换几台机器测。
风里有梦 2008-02-29
  • 打赏
  • 举报
回复
另外注:

/////////////////////////////////////////////////////////////////////////////
// 低层数据类型

typedef __int8 int8;
typedef __int16 int16;
typedef __int32 int32;
typedef __int64 int64;

typedef unsigned __int8 uint8;
typedef unsigned __int16 uint16;
typedef unsigned __int32 uint32;
typedef unsigned __int64 uint64;

typedef uint8 bool8;
typedef uint16 bool16;
typedef uint32 bool32;
typedef uint64 bool64;
风里有梦 2008-02-29
  • 打赏
  • 举报
回复
当然有启动, 你们上面提到的都没有问题, 我都有经过几天的测试完善的, 一切都没有问题, 就是退出时死锁的问题. 也就是我要完全调用CWorker的析构时才出现问题. 我怀疑是_endthreaqdex根本就没有通知我的WaitForSingleObject的等待, 所以一直在等待, 如果我不加这个等待,我又不能确认那些已启动的线程几时退出并释放线程的内存堆空间, 如果不等待我就直接销毁CWorker, 结果我想大家都会知道...

我还是把完整的代码贴出来吧, 让大家帮我分析一下.

宏定义:

#define _ZeroObj(clsName) \
::ZeroMemory( this, sizeof(##clsName) )


CWorker.h

//////////////////////////////////////////////////////////////
typedef bool32 (*PFNTHREADPROC)(void*);

typedef enum enumThreadStatus :int32
{
tsNull = 0,
tsTerminated,
tsExiting,
tsPause,
tsRunning
}ThreadStatusEnum;

typedef enum enumThreadPriority :int32
{
tpNull = 0,
tpIdle = THREAD_PRIORITY_IDLE,
tpLowest = THREAD_PRIORITY_LOWEST,
tpBelowNormal = THREAD_PRIORITY_BELOW_NORMAL,
tpNormal = THREAD_PRIORITY_NORMAL,
tpAboveNormal = THREAD_PRIORITY_ABOVE_NORMAL,
tpHighest = THREAD_PRIORITY_HIGHEST,
tpTimeCritical = THREAD_PRIORITY_TIME_CRITICAL

}ThreadPriorityEnum;

////////////////////////////////////////////////////////////
class PACTKERNELAPI CWorker
{
public:

//////////////构造和析构/////////////////
CWorker();
CWorker( PFNTHREADPROC pfnTheadProc, void* pvParam = NULL, uint32 uExecuFrequency = 0, bool32 bSuspended = FALSE );
~CWorker();

//////////////常用方法////////////
bool32 SetJob( PFNTHREADPROC pfnTheadProc, void* pvParam = NULL, uint32 uExecuFrequency = 0, bool32 bSuspended = FALSE );

void Pause()
{ if (m_hThread && IsRunning()){::SuspendThread(m_hThread); m_eThreadStatus = tsPause;} }

void Resume()
{ if (m_hThread && IsPause()){::ResumeThread(m_hThread); m_eThreadStatus = tsRunning;} }

void Sleep( DWORD dwMilliseconds )
{ ::Sleep( dwMilliseconds ); }

void EndJob( bool32 bWaitExit = FALSE );

/////////工作线程级别////////

ThreadPriorityEnum GetPriority()
{ return (ThreadPriorityEnum)::GetThreadPriority(m_hThread); }

bool32 SetPriority( ThreadPriorityEnum tp )
{ if (!tp) return GetPriority(); return (bool32)::SetThreadPriority(m_hThread, (int)tp); }

/////工作线程执行的频率(心跳时间)//////////

void SetFrequency( uint32 uExecuFreq ){ m_uExecuFrequency = uExecuFreq; }
uint32 GetFrequency(){ return m_uExecuFrequency; }

////////////线程句柄和ID//////////////

HANDLE GetThreadHandle() { return m_hThread; }
uint32 GetThreadId() { return m_uThreadId; }

///////////工作线程函数指针和它的参数/////////

PFNTHREADPROC GetThreadProc()
{ return m_pfnThreadProc; }

void* GetThreadParam()
{ return m_pvParam; }

/////////////工作线程状态/////////////////

ThreadStatusEnum GetStatus()
{ return m_eThreadStatus; }

//////////////状态检查//////////////////////

bool32 IsRunning()
{ return tsRunning == m_eThreadStatus; }

bool32 IsTerminated()
{ return tsTerminated == m_eThreadStatus; }

bool32 IsPause()
{ return tsPause == m_eThreadStatus; }

bool32 IsExiting()
{ return tsExiting == m_eThreadStatus; }

///////////////////////////////////////////

private:
//////////////////////////////////////////////////////

static unsigned __stdcall _ThreadProc( void* pvParam );

bool32 _SetJob( PFNTHREADPROC pfnTheadProc, void* pvParam, uint32 uExecuFrequency, bool32 bSuspended );

void _SetStatus( ThreadStatusEnum ts )
{ m_eThreadStatus = ts; }

void _Init();

//////////////////////////////////////////////////////
HANDLE m_hThread;
void* m_pvParam;
//CCSLock* m_pCSLock;
uint32 m_uThreadId;
ThreadStatusEnum m_eThreadStatus;
PFNTHREADPROC m_pfnThreadProc;
uint32 m_uExecuFrequency;
};
////////////////////////////////////////////////////////////

CWorker.cpp:

//////////////////////////////////////////////////////////////////////////
CWorker::CWorker()
{
_Init();
}

CWorker::CWorker( PFNTHREADPROC pfnTheadProc, void* pvParam, uint32 uExecuFrequency, bool32 bSuspended )
{
_Init();
_SetJob( pfnTheadProc, pvParam, uExecuFrequency, bSuspended );
}

CWorker::~CWorker()
{
EndJob(TRUE);
//_SafeDelete(m_pCSLock)
}

inline void CWorker::_Init()
{
_ZeroObj(CWorker);
//m_pCSLock = new CCSLock;
}
///////////////////////////////////////////////////////////////////////////

bool32 CWorker::SetJob( PFNTHREADPROC pfnTheadProc, void* pvParam, uint32 uExecuFrequency, bool32 bSuspended )
{
if ( GetStatus() > tsTerminated )
return FALSE;

return _SetJob( pfnTheadProc, pvParam, uExecuFrequency, bSuspended );
}

void CWorker::EndJob( bool32 bWaitExit )
{
if ( GetStatus() < tsPause )
return;

Resume();
_SetStatus( tsExiting );

DWORD dwTimeout = 3000;

if ( bWaitExit )
{
DWORD dwRet = ::WaitForSingleObject( m_hThread, dwTimeout );
switch ( dwRet )
{
case WAIT_TIMEOUT:
{
::TerminateThread( m_hThread, 0 );
m_hThread = NULL;
break;
}
case WAIT_OBJECT_0:
{
_SafeObjFree(::CloseHandle, m_hThread)
break;
}
}
}

m_uThreadId = 0;
}

//////////////////////////////////////////////////////////////////////////
// SetJob
inline bool32 CWorker::_SetJob( PFNTHREADPROC pfnTheadProc, void* pvParam, uint32 uExecuFrequency, bool32 bSuspended )
{
_SetStatus( bSuspended ? tsPause : tsNull );

m_uExecuFrequency = uExecuFrequency;

m_pfnThreadProc = pfnTheadProc;
m_pvParam = pvParam;

m_hThread = (HANDLE)chBEGINTHREADEX( NULL, 0, CWorker::_ThreadProc, this, CREATE_SUSPENDED, &m_uThreadId );

if ( !m_hThread )
{
_SetStatus( tsNull );
return FALSE;
}

if ( tsPause != GetStatus() )
{
::ResumeThread( m_hThread );
_SetStatus( tsRunning );
}

return TRUE;
}
//////////////////////////////////////////////////////////////////////////
unsigned __stdcall CWorker::_ThreadProc( void* pvParam )
{
CWorker* pWorker = (CWorker*) pvParam;

if ( pWorker->GetStatus() != tsRunning )
goto ExitFlags;

//pWorker->m_pCSLock->Enter();
PFNTHREADPROC pfnThreadProc = pWorker->GetThreadProc();

if ( !pfnThreadProc )
goto ExitFlags;

if ( !pWorker->GetFrequency() )
{
while ( pWorker->GetStatus() > tsExiting )
{
if ( pWorker->IsRunning() )
{
//pWorker->m_pCSLock->Enter();
(*pfnThreadProc) ( (void*)pWorker->GetThreadParam() );
//pWorker->m_pCSLock->Leave();
}
pWorker->Sleep( 30 );
}
}
else
{
DWORD dwPrev = ::timeGetTime() + pWorker->GetFrequency();

while ( pWorker->GetStatus() > tsExiting )
{
if ( pWorker->IsRunning() && dwPrev < ::timeGetTime() )
{
//pWorker->m_pCSLock->Enter();
(*pfnThreadProc) ( pWorker->GetThreadParam() );
//pWorker->m_pCSLock->Leave();

dwPrev = ::timeGetTime() + pWorker->GetFrequency();
}
pWorker->Sleep( 30 );
}
}

ExitFlags:
pWorker->_SetStatus( tsTerminated );
//_endthreadex( 0 );

//pWorker->m_pCSLock->Leave();

return 0;
}
//////////////////////////////////////////////////////////////////////////
sheenl 2008-02-29
  • 打赏
  • 举报
回复
突然发现你的线程SetJob是CREATE_SUSPENDED, 你是什么时候让这些线程工作起来的? 你确定在你停止线程的时候, 这些线程是工作的吗? 如果用主线程WaitSingleObject去等一个suspended的线程, 而这个suspended的线程要等待主线程用resumethread来启动它, 自然就死锁了。

michney 2008-02-29
  • 打赏
  • 举报
回复
这个跟多核无关,
看代码貌似问题不大
你开那一百个线程是怎么个开发?
我觉得有可能是线程根本就没有启动,第一个thread开启以后,就被抢占了
风里有梦 2008-02-29
  • 打赏
  • 举报
回复
如真是我所猜想的那样的话, 还是不能将静态线程方法让_beginthreadex共享启动. 为每一个_beginthreadex赋一个独立的线程函数才能避免这个共享时产生的退出死锁问题....
风里有梦 2008-02-29
  • 打赏
  • 举报
回复
难道CSDN没有一个懂得线程真正的执行原理的吗? 汗一个先!!!

因为我当初开一到二个线程时,绝对100%没有PROBLEM, 因为我的CPU是双核的, 但超过2个就有问题了,随着线程的增加,问题发生的频率越高! 因这个原因,我怀疑是线程在执行静态线程方法时, 当我在改变当前状态为tsExiting标志时, 可能导致所有的执行线程乱了, 所以才导致在线程退出时产生了死锁...

希望能有人出来指点我一下吧. 哎!!!
风里有梦 2008-02-29
  • 打赏
  • 举报
回复
42:

是的, 这个东东的作用我明白, 多谢指教.
风里有梦 2008-02-29
  • 打赏
  • 举报
回复
41:

while是执行工作线程外部的函数体, 这个好处就是, 重复定时间隔的执行指定的外部的函数代码, 而且外部函数不需要编写任何的while. 相当于一个计时器, 但这是一个线程计时器, 不是windows的WM_TIMER. 另外我写了两个while是因为当不需要间隔时间时, 可以去掉那些代码, 让程序执行的更快些!

另外你还是没有看我的代码, 这个pWorker->GetStatus()是线程的状态, 改变状态就是由EndJob发出tsExiting等...
风里有梦 2008-02-29
  • 打赏
  • 举报
回复
OK! 明白原因了...

感谢sheenl的帮助!!!

我的类真的是没有问题, 问题是出现在我的管理类, 呵呵, 我为此CWorker还编写了一个管理类(CWorkerMgr), 当我EndAllJob()时, 100个多个的线程, 每一个都要等待一个线程真正的完成后退出, 所以耗费大量的主线程的时间!!!

我重新编写了EndAllJob()方法, 以及修改了CWorker::EndJob(), 让此方法更灵活的适用于EndAllJob()的WaitForMultipleObjects(), 现在一试, 果真OK了!!! 100个线程, 1点延迟都没有,很正常的就退出了, 也没有死锁了, OK, 接下来要处理的就是EndAllJob()后的马上~CWorker()代码, 这个代码一定要保证所有的线程能完完全全的从内存中消失, 才能正确无错的析构, 要不然又会出现内存访问错误了...
linux_328 2008-02-29
  • 打赏
  • 举报
回复
CCSLock 是自己封装的类?
看方法好像你的线程要访问零界资源。
CCSLock.Enter()
和CCSLock.Leave()必须是原子操作,如果CCSLock是自己实现的方法,你必须保证它是原子操作。否则很容易招成死锁
加载更多回复(25)

64,648

社区成员

发帖
与我相关
我的任务
社区描述
C++ 语言相关问题讨论,技术干货分享,前沿动态等
c++ 技术论坛(原bbs)
社区管理员
  • C++ 语言社区
  • encoderlee
  • paschen
加入社区
  • 近7日
  • 近30日
  • 至今
社区公告
  1. 请不要发布与C++技术无关的贴子
  2. 请不要发布与技术无关的招聘、广告的帖子
  3. 请尽可能的描述清楚你的问题,如果涉及到代码请尽可能的格式化一下

试试用AI创作助手写篇文章吧