如何让timer_create与timer_delete实现多线程安全,避免死锁?
问题描述
我遇到一个特殊问题:在一个线程中调用timer_create创建定时器,另一个线程调用timer_delete删除该定时器时,程序死锁无法正常运行。不清楚该怎么实现线程安全,是用mutex(互斥锁),还是其他方式避免死锁?我的代码如下:
#include <iostream> #include <stdlib.h> #include <unistd.h> #include <errno.h> #include <fcntl.h> #include <unistd.h> #include <signal.h> #include <time.h> #include "pthread.h" typedef struct { struct sigevent tmrSigEvt; timer_t tmr; }TMR_DATA_T; int a = 10; int b = 20; int c = 0; TMR_DATA_T tmrDataArray[5] = { 0 }; void TimerCallback( union sigval timer_data ) { std::cout<<"am i here\n"; c = a + b; } int32_t CreateTimer( int32_t timerId, const uint32_t msPeriod ) { int32_t ret = -1; uint8_t slot = timerId; timer_t *tmrPtr = &tmrDataArray[slot].tmr; struct itimerspec period; uint32_t tmpPeriod; tmrDataArray[slot].tmrSigEvt.sigev_notify = SIGEV_THREAD; tmrDataArray[slot].tmrSigEvt.sigev_notify_function = TimerCallback; tmrDataArray[slot].tmrSigEvt.sigev_signo = timerId; tmrDataArray[slot].tmrSigEvt.sigev_value.sival_ptr = ( void* )&tmrDataArray[slot]; tmrDataArray[slot].tmrSigEvt.sigev_notify_attributes = NULL; ret = timer_create( /*CLOCK_REALTIME*/ CLOCK_MONOTONIC, &tmrDataArray[slot].tmrSigEvt, tmrPtr ); std::cout<<"----------------DO I COME HERE AFTER CREATING THE TIMER---------\n"; tmpPeriod = msPeriod % 1000; // remainder or period less than 1sec period.it_value.tv_nsec = ( long )( tmpPeriod * 1000000 ); // nano-seconds period.it_interval.tv_sec = 0; // non-repeating timer period.it_interval.tv_nsec = 0; ret = timer_settime( *tmrPtr, 0, &period, NULL ); return ret; } int32_t DeleteTimer( int32_t timerId ) { int32_t ret = -1; if ( NULL != tmrDataArray[timerId].tmr ) { ret = timer_delete( tmrDataArray[timerId].tmr ); std::cout<<"Deleting the timer inside ------\n"; } else { std::cout<<"failed to delete the timer\n"; } return ret; } void *Create_Timer_thread1( void *ptr ) { std::cout<<"Creating a timer\n"; CreateTimer( 2, 100 ); std::cout<<"done creating a timer\n"; return NULL; } void *Delete_Timer_thread2( void *ptr ) { std::cout<<"second thread deleting a timer\n"; DeleteTimer(2); return NULL; } int main(int argc, char const *argv[]) { pthread_t thread1, thread2; int iret1, iret2; iret1 = pthread_create( &thread1, NULL, Create_Timer_thread1, NULL); std::cout<<"creating the second thread\n"; iret2 = pthread_create( &thread2, NULL, Delete_Timer_thread2, NULL); pthread_join( thread1, NULL); pthread_join( thread2, NULL); std::cout<<"the value of c is: "<<c<<"\n"; return 0; }
问题分析与解决
核心原因
- 竞态条件:线程调度顺序不确定,删除线程可能在定时器未完成初始化时就执行
timer_delete,同时tmrDataArray全局数组无保护访问,导致状态不一致。 SIGEV_THREAD的隐式阻塞:使用该模式时,timer_delete会等待回调线程执行完毕才返回,若此时回调线程与删除线程形成资源依赖,极易触发死锁。- 错误的句柄判断:
timer_t是整数类型,原代码用NULL != tmrDataArray[timerId].tmr判断初始化状态是错误的,应该用!= 0。
解决方法
1. 互斥锁保护核心操作(最通用方案)
用全局互斥锁保护定时器的创建、删除操作,以及共享资源的访问,确保同一时间只有一个线程操作相关状态:
// 全局互斥锁,初始化默认属性 pthread_mutex_t timer_mutex = PTHREAD_MUTEX_INITIALIZER; int32_t CreateTimer( int32_t timerId, const uint32_t msPeriod ) { int32_t ret = -1; pthread_mutex_lock(&timer_mutex); // 加锁 uint8_t slot = timerId; timer_t *tmrPtr = &tmrDataArray[slot].tmr; struct itimerspec period; uint32_t tmpPeriod; tmrDataArray[slot].tmrSigEvt.sigev_notify = SIGEV_THREAD; tmrDataArray[slot].tmrSigEvt.sigev_notify_function = TimerCallback; tmrDataArray[slot].tmrSigEvt.sigev_signo = timerId; tmrDataArray[slot].tmrSigEvt.sigev_value.sival_ptr = ( void* )&tmrDataArray[slot]; tmrDataArray[slot].tmrSigEvt.sigev_notify_attributes = NULL; ret = timer_create(CLOCK_MONOTONIC, &tmrDataArray[slot].tmrSigEvt, tmrPtr); std::cout<<"----------------DO I COME HERE AFTER CREATING THE TIMER---------\n"; tmpPeriod = msPeriod % 1000; period.it_value.tv_nsec = ( long )( tmpPeriod * 1000000 ); period.it_interval.tv_sec = 0; period.it_interval.tv_nsec = 0; ret = timer_settime( *tmrPtr, 0, &period, NULL ); pthread_mutex_unlock(&timer_mutex); // 解锁 return ret; } int32_t DeleteTimer( int32_t timerId ) { int32_t ret = -1; pthread_mutex_lock(&timer_mutex); // 加锁 if ( 0 != tmrDataArray[timerId].tmr ) // 修正句柄判断逻辑 { ret = timer_delete( tmrDataArray[timerId].tmr ); std::cout<<"Deleting the timer inside ------\n"; tmrDataArray[timerId].tmr = 0; // 删除后重置状态,避免重复操作 } else { std::cout<<"failed to delete the timer\n"; } pthread_mutex_unlock(&timer_mutex); // 解锁 return ret; } // 回调函数也要加锁保护全局变量c void TimerCallback( union sigval timer_data ) { pthread_mutex_lock(&timer_mutex); std::cout<<"am i here\n"; c = a + b; pthread_mutex_unlock(&timer_mutex); } // main函数末尾销毁互斥锁 int main(int argc, char const *argv[]) { pthread_t thread1, thread2; int iret1, iret2; iret1 = pthread_create( &thread1, NULL, Create_Timer_thread1, NULL); std::cout<<"creating the second thread\n"; iret2 = pthread_create( &thread2, NULL, Delete_Timer_thread2, NULL); pthread_join( thread1, NULL); pthread_join( thread2, NULL); std::cout<<"the value of c is: "<<c<<"\n"; pthread_mutex_destroy(&timer_mutex); // 销毁锁 return 0; }
2. 避免timer_delete阻塞的替代方案
如果不需要等待回调线程结束,可以改用SIGEV_SIGNAL模式,自行处理信号回调,此时timer_delete不会阻塞等待回调执行。但这种方式需要处理信号的线程安全问题,复杂度较高,适合对延迟敏感的场景。
3. 条件变量同步创建与删除(特定场景)
用条件变量让删除线程等待创建线程完成定时器初始化后再执行,适合严格控制调度顺序的场景,但灵活性不如互斥锁。
内容的提问来源于stack exchange,提问作者newww
相关产品推荐
相关产品推荐

