zoukankan      html  css  js  c++  java
  • Linux系统编程(29)——线程间同步(续篇)


    线程间的同步还有这样一种情况:线程A需要等某个条件成立才能继续往下执行,现在这个条件不成立,线程A就阻塞等待,而线程B在执行过程中使这个条件成立了,就唤醒线程A继续执行。在pthread库中通过条件变量(Condition Variable)来阻塞等待一个条件,或者唤醒等待这个条件的线程。Condition Variable用pthread_cond_t类型的变量表示,可以这样初始化和销毁:

    #include <pthread.h>

    int pthread_cond_destroy(pthread_cond_t*cond);

    int pthread_cond_init(pthread_cond_t*restrict cond,

          const pthread_condattr_t *restrict attr);

    pthread_cond_t cond =PTHREAD_COND_INITIALIZER;

    返回值:成功返回0,失败返回错误号。

    和Mutex的初始化和销毁类似,pthread_cond_init函数初始化一个Condition Variable,attr参数为NULL则表示缺省属性,pthread_cond_destroy函数销毁一个Condition Variable。如果Condition Variable是静态分配的,也可以用宏定义PTHEAD_COND_INITIALIZER初始化,相当于用pthread_cond_init函数初始化并且attr参数为NULL。ConditionVariable的操作可以用下列函数:

    #include <pthread.h>

    int pthread_cond_timedwait(pthread_cond_t*restrict cond,

          pthread_mutex_t *restrict mutex,

          const struct timespec *restrict abstime);

    int pthread_cond_wait(pthread_cond_t*restrict cond,

          pthread_mutex_t *restrict mutex);

    int pthread_cond_broadcast(pthread_cond_t*cond);

    int pthread_cond_signal(pthread_cond_t*cond);

    返回值:成功返回0,失败返回错误号。

    可见,一个Condition Variable总是和一个Mutex搭配使用的。一个线程可以调用pthread_cond_wait在一个Condition Variable上阻塞等待,这个函数做以下三步操作:

    1、释放Mutex

    2、阻塞等待

    3、当被唤醒时,重新获得Mutex并返回

    pthread_cond_timedwait函数还有一个额外的参数可以设定等待超时,如果到达了abstime所指定的时刻仍然没有别的线程来唤醒当前线程,就返回ETIMEDOUT。一个线程可以调用pthread_cond_signal唤醒在某个Condition Variable上等待的另一个线程,也可以调用pthread_cond_broadcast唤醒在这个Condition Variable上等待的所有线程。

    下面的程序演示了一个生产者-消费者的例子,生产者生产一个结构体串在链表的表头上,消费者从表头取走结构体。

    #include <stdlib.h>

    #include <pthread.h>

    #include <stdio.h>

    struct msg {

             structmsg *next;

             intnum;

    };

    struct msg *head;

    pthread_cond_t has_product =PTHREAD_COND_INITIALIZER;

    pthread_mutex_t lock =PTHREAD_MUTEX_INITIALIZER;

    void *consumer(void *p)

    {

             structmsg *mp;

             for(;;) {

                       pthread_mutex_lock(&lock);

                       while(head == NULL)

                                pthread_cond_wait(&has_product,&lock);

                       mp= head;

                       head= mp->next;

                       pthread_mutex_unlock(&lock);

                       printf("Consume%d ", mp->num);

                       free(mp);

                       sleep(rand()% 5);

             }

    }

    void *producer(void *p)

    {

             structmsg *mp;

             for(;;) {

                       mp= malloc(sizeof(struct msg));

                       mp->num= rand() % 1000 + 1;

                       printf("Produce%d ", mp->num);

                       pthread_mutex_lock(&lock);

                       mp->next= head;

                       head= mp;

                       pthread_mutex_unlock(&lock);

                       pthread_cond_signal(&has_product);

                       sleep(rand()% 5);

             }

    }

    int main(int argc, char *argv[])

    {

             pthread_tpid, cid; 

             srand(time(NULL));

             pthread_create(&pid,NULL, producer, NULL);

             pthread_create(&cid,NULL, consumer, NULL);

             pthread_join(pid,NULL);

             pthread_join(cid,NULL);

             return0;

    }执行结果如下:

    Produce 744

    Consume 744

    Produce 567

    Produce 881

    Consume 881

    Produce 911

    Consume 911

    Consume 567

    Produce 698

    Consume 698

    在这个例子中,生产者和消费者访问链表的顺序是LIFO的。

    Mutex变量是非0即1的,可看作一种资源的可用数量,初始化时Mutex是1,表示有一个可用资源,加锁时获得该资源,将Mutex减到0,表示不再有可用资源,解锁时释放该资源,将Mutex重新加到1,表示又有了一个可用资源。

    信号量(Semaphore)和Mutex类似,表示可用资源的数量,和Mutex不同的是这个数量可以大于1。

    这里介绍的是POSIX semaphore库函数,详见sem_overview(7),这种信号量不仅可用于同一进程的线程间同步,也可用于不同进程间的同步。

    #include <semaphore.h>

    int sem_init(sem_t *sem, int pshared,unsigned int value);

    int sem_wait(sem_t *sem);

    int sem_trywait(sem_t *sem);

    int sem_post(sem_t * sem);

    int sem_destroy(sem_t * sem);

    semaphore变量的类型为sem_t,sem_init()初始化一个semaphore变量,value参数表示可用资源的数量,pshared参数为0表示信号量用于同一进程的线程间同步,本节只介绍这种情况。在用完semaphore变量之后应该调用sem_destroy()释放与semaphore相关的资源。

    调用sem_wait()可以获得资源,使semaphore的值减1,如果调用sem_wait()时semaphore的值已经是0,则挂起等待。如果不希望挂起等待,可以调用sem_trywait()。调用sem_post()可以释放资源,使semaphore的值加1,同时唤醒挂起等待的线程。

    上面生产者-消费者的例子是基于链表的,其空间可以动态分配,现在基于固定大小的环形队列重写这个程序:

    #include <stdlib.h>

    #include <pthread.h>

    #include <stdio.h>

    #include <semaphore.h>

    #define NUM 5

    int queue[NUM];

    sem_t blank_number, product_number;

    void *producer(void *arg)

    {

             intp = 0;

             while(1) {

                       sem_wait(&blank_number);

                       queue[p]= rand() % 1000 + 1;

                       printf("Produce%d ", queue[p]);

                       sem_post(&product_number);

                       p= (p+1)%NUM;

                       sleep(rand()%5);

             }

    }

    void *consumer(void *arg)

    {

             intc = 0;

             while(1) {

                       sem_wait(&product_number);

                       printf("Consume%d ", queue[c]);

                       queue[c]= 0;

                       sem_post(&blank_number);

                       c= (c+1)%NUM;

                       sleep(rand()%5);

             }

    }

    int main(int argc, char *argv[])

    {

             pthread_tpid, cid; 

             sem_init(&blank_number,0, NUM);

             sem_init(&product_number,0, 0);

             pthread_create(&pid,NULL, producer, NULL);

             pthread_create(&cid,NULL, consumer, NULL);

             pthread_join(pid,NULL);

             pthread_join(cid,NULL);

             sem_destroy(&blank_number);

             sem_destroy(&product_number);

             return0;

    }

  • 相关阅读:
    关闭Axapta
    使用API操纵文件
    调用数据源里面的方法
    Axapta进度条应用
    过滤Form的数据源
    使用系统自动产生编号
    获取表中的所有字段
    打开浏览器URL
    报表的一些常用选项
    .NET中访问Oracle数据库链接:ORA02041: client database did not begin a transaction 问题的处理。
  • 原文地址:https://www.cnblogs.com/new0801/p/6176973.html
Copyright © 2011-2022 走看看