1:pthread_create 函数的安全使用问题
做过linux多线程开发的人都会用过pthread_create函数,但是很少人会注意到:主线程在使用pthread_create创建线程时,如果pthread_create 函数的第四个参数(指针参数)传入的值会被主线程随时修改时,这时我们的线程从pthread_create 函数的第四个参数获取的值可能就不是我们想要传入的值了。
看看下面代码:在 main 函数的 for 循环中创建10个线程;每创建一个线程时都把循环变量 i 的值通过 pthread_create 函数的第四个参数传入线程函数中;但是线程获取的值可能不并不是我们想传入的值。
#include <pthread.h>
#include <stdio.h>
void * threadFunc( void * pm_iPthreadId )
{
unsigned int iId = * ( unsigned int * ) pm_iPthreadId;
printf( "### No.%d pthread start exec; pthread id = %u !/n", iId, pthread_self() );
time_t iStartTime = time( NULL );
while(1)
{
time_t iTempTime = time( NULL );
if ( iTempTime - iStartTime > 2 )
{
printf( "$$$ NO.%d pthread cirle exec !/n", * ( unsigned int * ) pm_iPthreadId );
iStartTime = time( NULL );
}
}
}
int main()
{
pthread_t * pthreadId;
pthreadId = new pthread_t[10];
for( unsigned int i = 0; i < 10; i++ )
{
int iRet = pthread_create( pthreadId + i, NULL, threadFunc, &i );
if ( iRet == 0 )
{
printf( "@@@ create No.%d pthread success; pthreadId = %u !/n", i, pthreadId + i );
}
}
while(1)
{
}
return 0;
}
程序运行结果如下(结果是随机的,每次运行可能都不一样):
@@@ create No.0 pthread success; pthreadId = 138174472 !
### No.1 pthread start exec; pthread id = 3076803472 !
### No.1 pthread start exec; pthread id = 3087293328 !
@@@ create No.1 pthread success; pthreadId = 138174476 !
@@@ create No.2 pthread success; pthreadId = 138174480 !
### No.3 pthread start exec; pthread id = 3055823760 !
### No.3 pthread start exec; pthread id = 3066313616 !
@@@ create No.3 pthread success; pthreadId = 138174484 !
### No.4 pthread start exec; pthread id = 3045333904 !
@@@ create No.4 pthread success; pthreadId = 138174488 !
@@@ create No.5 pthread success; pthreadId = 138174492 !
### No.6 pthread start exec; pthread id = 3024354192 !
### No.6 pthread start exec; pthread id = 3034844048 !
@@@ create No.6 pthread success; pthreadId = 138174496 !
@@@ create No.7 pthread success; pthreadId = 138174500 !
### No.8 pthread start exec; pthread id = 3003374480 !
### No.8 pthread start exec; pthread id = 3013864336 !
@@@ create No.8 pthread success; pthreadId = 138174504 !
@@@ create No.9 pthread success; pthreadId = 138174508 !
### No.10 pthread start exec; pthread id = 2992884624 !
$$$ NO.10 pthread cirle exec !
$$$ NO.10 pthread cirle exec !
$$$ NO.10 pthread cirle exec !
$$$ NO.10 pthread cirle exec !
$$$ NO.10 pthread cirle exec !
$$$ NO.10 pthread cirle exec !
$$$ NO.10 pthread cirle exec !
$$$ NO.10 pthread cirle exec !
$$$ NO.10 pthread cirle exec !
$$$ NO.10 pthread cirle exec !
$$$ NO.10 pthread cirle exec !
$$$ NO.10 pthread cirle exec !
$$$ NO.10 pthread cirle exec !
$$$ NO.10 pthread cirle exec !
$$$ NO.10 pthread cirle exec !
从以“###”开头的字符结果可以看出:以“###No.0”、“###No.2”、“###No.5、“###No.7”、“###No.7” 的字符没有;从这可以看出,我们从pthread_create 函数的第四个参数传入的值是不安全的,应该受到安全保护。
2:此问题原因
pthread_create 函数是非阻塞函数;会立即返回,主线程不会在创建线程后阻塞到线程函数执行完后返回,导致线程函数获取到循环变量 i 的值被主线程立即改变。
3:此问题解决方法
利用条件变量的保护,在线程函数中把 pthread_create 第四个参数传入的值保存为局部变量。
#include <pthread.h>
#include <stdio.h>
pthread_cond_t m_Cond;
pthread_mutex_t m_Locker;
void * threadFunc( void * pm_iPthreadId )
{
pthread_mutex_lock(&m_Locker);
unsigned int iId = * ( unsigned int * ) pm_iPthreadId;
printf( "### No.%d pthread start exec; pthread id = %u !/n", iId, pthread_self() );
pthread_cond_signal(&m_Cond);
pthread_mutex_unlock(&m_Locker);
time_t iStartTime = time( NULL );
while(1)
{
time_t iTempTime = time( NULL );
if ( iTempTime - iStartTime > 2 )
{
printf( "$$$ NO.%d pthread cirle exec !/n", * ( unsigned int * ) pm_iPthreadId );
iStartTime = time( NULL );
}
}
}
int main()
{
pthread_mutex_init(&m_Locker, NULL);
pthread_cond_init(&m_Cond, NULL);
pthread_t * pthreadId;
pthreadId = new pthread_t[10];
for( unsigned int i = 0; i < 10; i++ )
{
pthread_mutex_lock(&m_Locker);
int iRet = pthread_create( pthreadId + i, NULL, threadFunc, &i );
if ( iRet == 0 )
{
printf( "@@@ create No.%d pthread success; pthreadId = %u !/n", i, pthreadId + i );
}
pthread_cond_wait(&m_Cond, &m_Locker);
pthread_mutex_unlock(&m_Locker);
}
while(1)
{
}
return 0;
}
上面代码运行结果:
@@@ create No.0 pthread success; pthreadId = 147902472 !
### No.0 pthread start exec; pthread id = 3087563664 !
@@@ create No.1 pthread success; pthreadId = 147902476 !
### No.1 pthread start exec; pthread id = 3077073808 !
@@@ create No.2 pthread success; pthreadId = 147902480 !
### No.2 pthread start exec; pthread id = 3066583952 !
@@@ create No.3 pthread success; pthreadId = 147902484 !
### No.3 pthread start exec; pthread id = 3056094096 !
@@@ create No.4 pthread success; pthreadId = 147902488 !
### No.4 pthread start exec; pthread id = 3045604240 !
@@@ create No.5 pthread success; pthreadId = 147902492 !
### No.5 pthread start exec; pthread id = 3035114384 !
@@@ create No.6 pthread success; pthreadId = 147902496 !
### No.6 pthread start exec; pthread id = 3024624528 !
@@@ create No.7 pthread success; pthreadId = 147902500 !
### No.7 pthread start exec; pthread id = 3014134672 !
@@@ create No.8 pthread success; pthreadId = 147902504 !
### No.8 pthread start exec; pthread id = 3003644816 !
@@@ create No.9 pthread success; pthreadId = 147902508 !
### No.9 pthread start exec; pthread id = 2993154960 !
$$$ NO.10 pthread cirle exec !
$$$ NO.10 pthread cirle exec !
$$$ NO.10 pthread cirle exec !
$$$ NO.10 pthread cirle exec !
$$$ NO.10 pthread cirle exec !
$$$ NO.10 pthread cirle exec !
$$$ NO.10 pthread cirle exec !
$$$ NO.10 pthread cirle exec !
$$$ NO.10 pthread cirle exec !
$$$ NO.10 pthread cirle exec !
$$$ NO.10 pthread cirle exec !
$$$ NO.10 pthread cirle exec !
$$$ NO.10 pthread cirle exec !