多线程编程注意要点

本文介绍了一种通过设置标记来安全地中断线程的方法,并强调了多线程对象配对使用的重要性,以及线程间数据传递时应遵循的原则。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

1.不能直接中断线程,应该设置一个标记,线程运行时检查这个标记

void CThreadSoap::ThreadRun()
{
RESULTTYPE rc(RC_SUCCESS);
//线程是否运行线程
bool is_run(false);
//线程要执行的动作
long thread_action(0);
//要执行动作的参数
void *lpActionPara( NULL );
//窗体句柄
HWND hWnd( NULL );

//gSaop对象
struct soap gSoap;

//获取线程是否运行
rc = this->CheckStatus( is_run,thread_action,&hWnd,&lpActionPara);
if( RC_SUCCESS != rc )
{
this->SetThreadError(rc);
return;
}
::soap_init(&gSoap);
while( is_run )
{
//检查是否执行动作
if( 0 != thread_action )
{
this->CallSoap( thread_action, gSoap,hWnd,lpActionPara );
this->ResetThreadAction();
this->SafePostMessage(hWnd,WM_SAOP_DONE,thread_action);
}
else
{
::Sleep(1);
}

//释放资源
hWnd = NULL;
CThreadBase::SafeFreeObject(lpActionPara);
//获取线程是否继续运行
rc = this->CheckStatus( is_run,thread_action,&hWnd,&lpActionPara);
if( RC_SUCCESS != rc )
{
this->SetThreadError( rc);
break;
}
}
//释放资源
CThreadBase::SafeFreeObject(lpActionPara);
::soap_done(&gSoap);
}
is_run是线程方法中的一个局部变量,每次循环时通过类的成员方法CheckStatus读取类成员变量(m_ThreadRun)决定线程是否继续运行.当要停止或退出线程时,只需要把类成员变量m_ThreadRun设置为false即可.
 
检查线程状态的方法
/获取线程执行动作的参数
RESULTTYPE CThreadBase::CheckStatus(bool &is_run,long &thread_action,HWND *lpHWnd,void **lppVal )
{
if( NULL == this->m_pthread_mutex || NULL == this->m_pthread_cond )
return ERR_INITIALIZE_FAIL;


bool thread_is_run(false); //线程是否运行
long thread_cur_action(0); //当前线程正在执行的动作
void *lpData( NULL ); //线程执行动作时的数据
HWND hWnd( NULL ); //窗体句柄

//锁定要读取的数据
::pthread_mutex_lock(&this->m_pthread_mutex);


if( 0 != this->m_ActionInfo.action )
{
//检查共享对象的有效性
if( this->m_ActionInfo.data.dwMemSize > 0 && NULL != this->m_ActionInfo.data.lpData)
{
//分配内存
lpData = malloc( this->m_ActionInfo.data.dwMemSize);
if( NULL == lpData )
{
::pthread_cond_signal( &this->m_pthread_cond );
::pthread_mutex_unlock(&this->m_pthread_mutex );
return ERR_MALLOC_FAIL;
}
memset( lpData,0x0, this->m_ActionInfo.data.dwMemSize );
memcpy(lpData,this->m_ActionInfo.data.lpData, this->m_ActionInfo.data.dwMemSize);
//当设置了动作参数时,当前线程复制对象完成后通知主线程释放资源
::pthread_cond_signal( &this->m_pthread_cond );
}
}
hWnd = this->m_ActionInfo.hWnd;
thread_cur_action = this->m_ActionInfo.action;
thread_is_run = this->m_ThreadRun;
::pthread_mutex_unlock(&this->m_pthread_mutex );

*lpHWnd = hWnd;
is_run = thread_is_run;
thread_action = thread_cur_action;
*lppVal = lpData;
return RC_SUCCESS;
}
 
停止线程
RESULTTYPE CThreadBase::Stop()
{
if( NULL == this->m_pthread_mutex )
return ERR_INITIALIZE_FAIL;
::pthread_mutex_lock( &this->m_pthread_mutex );
this->m_ThreadRun = false;
::pthread_mutex_unlock(&this->m_pthread_mutex );
//等待线程运行完成
::pthread_join(this->m_pthread,NULL);
return RC_SUCCESS;
}


2.多线程对象应该配对使用,例如pthread_mutex_t和pthread_rwlock_t对象,同一对象不能在写的时候用互斥对象锁定,在读取的时间用读写锁锁定,应该是在读写时配对使用pthread_mutex_t对象或pthread_rwlock_t对象,例如上例中的Stop方法和CheckStatus配对使用pthread_mutex_lock

3.内存释放应遵循谁创建谁释放的原则.例如有线程A和线程B两个对象,在线程A上分配的内存应在线程A上释放,不要在线程B上释放线程A分配内存


RESULTTYPE CThreadSoap::CallMapBasicInfo( const HWND &hWnd,const char *lpcszMapService )
{
if( NULL == this->m_pthread_mutex || NULL == this->m_pthread_cond )
return ERR_INITIALIZE_FAIL;
if( NULL == lpcszMapService )
return ERR_INVALID_ARGUMENTS;

bool thread_is_run;
long thread_cur_action;
size_t dwMemSize( ( strlen(lpcszMapService) + 1 ) * sizeof(char) );
//检查线程的状态
this->CheckStatus(thread_is_run,thread_cur_action);
if( false == thread_is_run )
return ERRPT_THREAD_NOTRUN;
if( 0 != thread_cur_action )
return ERRPT_THREAD_ACTION;

::pthread_mutex_lock( &this->m_pthread_mutex );
//注意action动作在调用完成后自动还原
this->m_ActionInfo.hWnd = hWnd;
this->m_ActionInfo.action = PT_ACTION_WEBSERVICE_MAPBASICINFO;
this->m_ActionInfo.data.dwMemSize = dwMemSize;
this->m_ActionInfo.data.lpData = malloc( dwMemSize );
if( NULL == this->m_ActionInfo.data.lpData )
{
this->m_ActionInfo.action = 0;
this->m_ActionInfo.data.dwMemSize = 0;
::pthread_mutex_unlock(&this->m_pthread_mutex );
return ERR_MALLOC_FAIL;
}
memset(this->m_ActionInfo.data.lpData,0x0, dwMemSize);
strcpy_s((char *)this->m_ActionInfo.data.lpData, dwMemSize,lpcszMapService );
//等待线程获取参数完成后释放资源
::pthread_cond_wait( &this->m_pthread_cond, &this->m_pthread_mutex );
//释放资源
//线程状态保持到Soap方法调用完成后还原ResetThreadAction
//this->m_ActionInfo.hWnd = NULL;
//this->m_ActionInfo.action = 0;
this->m_ActionInfo.data.dwMemSize = 0;
free(this->m_ActionInfo.data.lpData);
this->m_ActionInfo.data.lpData = NULL;

::pthread_mutex_unlock(&this->m_pthread_mutex );
return RC_SUCCESS;
}

在主线程中调用CallMapBasicInfo方法通知线程调用指定的Soap方法(一个url参数),在主线程中将传入的url参数保存到线程数据共享区(this->m_ActionInfo),然后等待线程读取共享区中的数据完成(CheckStatus方法),然后在主线程中释放共享区中的数据(因为它是在主线程中创建的)
4.线程间数据传递应使用共享内存.读取或写入共享内存时锁定内存(如前例).发送消息时消息参数只能使用基本数据类型(long,int,bool等),复杂的数据类型采用消息通知后从共享内存中获取,从而保证内存能正确释放.因为postmessage消息是发送到队列中的,极端情况下,如果postmessage的参数为指针,可能导致无法正确释放内存.

5.线程运行的方法内部不能直接使用类的成员变量,如果需要读写类成员变量,应通过互斥对象或读写锁对象,将类成员变量复制到方法中局部变量或将局部变量复制到类成员变量.注意是采用复制的方式,虽然效率稍差,但能保证线程安全.

6.pThread优先使用读写锁对象(pthread_rwlock_t),例如CallMapBasicInfo的返回成功用,用写锁(pthread_rwlock_wrlock)将返回值保存到共享区,获取时通过读锁(pthread_rwlock_rdlock)将共享区中的信息复制出来.

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

kmblack1

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值