You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

如何让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;
}

问题分析与解决

核心原因

  1. 竞态条件:线程调度顺序不确定,删除线程可能在定时器未完成初始化时就执行timer_delete,同时tmrDataArray全局数组无保护访问,导致状态不一致。
  2. SIGEV_THREAD的隐式阻塞:使用该模式时,timer_delete会等待回调线程执行完毕才返回,若此时回调线程与删除线程形成资源依赖,极易触发死锁。
  3. 错误的句柄判断: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

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.07.09 10:17:09