pthread_cond_timedwait&&pthread_cond_wait&&sigwait()

由于工作上的事情,要用到线程之间的同步,而且有超时处理,在网上看到了使用pthread_cond_timedwait()函数和pthread_cond_wait()函数,其实2个函数都差不多,我主要是要用pthread_cond_timedwait()函数。代替不可控的sleep函数。

pthread_cond_timedwait()函数有三个入口参数:

(1)pthread_cond_t __cond:条件变量(触发条件)

(2)pthread_mutex_t __mutex: 互斥锁

(3)struct timespec __abstime: 等待时间(其值为系统时间 + 等待时间)

当在指定时间内有信号传过来时,pthread_cond_timedwait()返回0,否则返回一个非0数(我没有找到返回值的定义);

在使用pthread_cond_timedwait()函数时,必须有三步:

1:加互斥锁:pthread_mutex_lock(&__mutex)

2:等待:pthread_cond_timedwait(&__cond, &__mutex, &__abstime)   //解锁->等待->加锁

3:解互斥锁:pthread_mutex_unlock(&__mutex)

发送信号量时,也要有三步:

1:加互斥锁:pthread_mutex_lock(&__mutex)

2:发送:pthread_cond_signal(&__cond)

3:解互斥锁:pthread_mutex_unlock(&__mutex)

pthread_cond_timedwait()在官方文档中介绍了按绝对时间等待超时,但是几乎没有对按相对等待做说明。然而绝对时间模式有个最致命的缺点,就是在设置等待时间的时候,若系统时间发生了调整,可能出现永远等不到超时的极端情况。使用相对时间可以避免上述问题。所以需要使用CLOCK_MONOTONIC,相对时间来防止时间戳跳变,非常安全。

在pthread_cond_wait时执行pthread_cancel后,要先在pthread_cleanup handler时要先解锁已与相应条件变量绑定的mutex。这样是为了保证pthread_cond_wait可以返回到调用线程。所以需要cleanup函数配套使用,pthread_cleanup_push(cleanup, NULL);注册cleanup。pthread_cleanup_pop(0);取消。

sigwait是同步的等待信号的到来,而不是像进程中那样是异步的等待信号的到来。sigwait函数使用一个信号集作为他的参数,并且在集合中的任一个信号发生时返回该信号值,解除阻塞,然后可以针对该信号进行一些相应的处理。在多线程代码中,总是使用sigwait或者sigwaitinfo或者sigtimedwait等函数来处理信号。而不是signal或者sigaction等函数。因为在一个线程中调用signal或者sigaction等函数会改变所以线程中的信号处理函数。而不是仅仅改变调用signal/sigaction的那个线程的信号处理函数。调用sigwait同步等待的信号必须在调用线程中被屏蔽,并且通常应该在所有的线程中被屏蔽(这样可以保证信号绝不会被送到除了调用sigwait的任何其它线程),这是通过利用信号掩码的继承关系来达到的。


代码如下

#include <stdio.h>
#include <pthread.h>
#include <time.h>
 
typedef struct mutex_cond
{
	pthread_condattr_t cattr;
        pthread_mutex_t i_mutex;
        pthread_cond_t i_cv;
		void* i_sigevent; 
}mutex_cond_t;
mutex_cond_t mcond;

//CLOCK_MONOTONIC 
int pthread_cond_timedwait_init(void){
    int ret = -1;
    ret = pthread_condattr_init(&(mcond.cattr));
    if (ret != 0)
    {
        printf("pthread_condattr_init failed %d\n", ret);
        return ret;
    }
    mcond.i_sigevent = NULL;
    ret = pthread_mutex_init ( &(mcond.i_mutex), NULL);
    ret = pthread_condattr_setclock(&(mcond.cattr), CLOCK_MONOTONIC);
    ret = pthread_cond_init(&(mcond.i_cv), &(mcond.cattr));

    return ret;
}

#define handle_error_en(en, msg)\
        do { errno= en; perror(msg);exit(EXIT_FAILURE);}while(0)

static void *sig_thread(void*arg)
{
      sigset_t *set=(sigset_t*) arg;
      int s, sig;

      for (;;){
            s = sigwait(set,&sig);
            if (s != 0)
                  handle_error_en(s,"sigwait");
            printf("Signal handling thread got signal %d\n", sig);
            //sent signal
            pthread_mutex_lock(&(mcond.i_mutex));
            pthread_cond_signal(&(mcond.i_cv));
            pthread_mutex_unlock(&(mcond.i_mutex));
      }
}


void cleanup(void *arg) 
{ 
    printf("cleanup !\n");
    pthread_mutex_unlock(&(mcond.i_mutex));
} 

// g++ -o pwait pwait.cpp -lpthread -lrt
int main()
{
        sigset_t set;
        int s;
        pthread_t sigwait_thread = NULL;

	pthread_cond_timedwait_init();

        sigemptyset(&set);
        sigaddset(&set, SIGUSR1);
        s = pthread_sigmask(SIG_BLOCK,&set,NULL);
        if (s!= 0)
            handle_error_en(s,"pthread_sigmask");
        s = pthread_create(&sigwait_thread,NULL,&sig_thread,(void*)&set);
        if (s!= 0)
            handle_error_en(s,"sig pthread_create");
 
    	struct timespec tv;
	while(1)
	{
	    pthread_testcancel();//若有取消信号,取消线程
            pthread_setcancelstate(PTHREAD_CANCEL_DISABLE,NULL);//不可取消线程,阻塞取消signal

            //do somethings
            
            pthread_setcancelstate(PTHREAD_CANCEL_ENABLE,NULL);//可以取消
            pthread_cleanup_push(cleanup, NULL);  // thread cleanup handler 
            pthread_mutex_lock(&(mcond.i_mutex));
            clock_gettime(CLOCK_MONOTONIC, &tv);
	    printf("now time:%d\n", tv.tv_sec);
	    tv.tv_sec += 60;// 设置20秒后没收到事件超时返回
	    ret = pthread_cond_timedwait(&(mcond.i_cv), &(mcond.i_mutex), &tv);
            pthread_mutex_unlock(&(mcond.i_mutex));
            pthread_cleanup_pop(0);

	}
	
	return 0;
}

此时发送信号给进程kill -11 pid,main会提前解除休眠。

那么什么是取消点呢?:

取消点是在程序在运行的时候检测是否收到取消请求,是否允许允许操作执行的点。下面的POSIX线程函数就是取消点:
pthread_join()
 pthread_cond_wait()
pthread_cond_timedwait()
 pthread_testcancel()
sem_wait()
sigwait()

还有很多

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章