关于线程间IPC

mutex:

多线程经常会出现访问冲突的情况,解决的办法是引入互斥锁。获得锁的线程可以完成“读-修改-写”的操作,然后释放锁给其它线程,没有获得锁的线程只能等待而不能访问共享数据,这样“读-修改-写”三步操作组成一个原子操作,要么都执行,要么都不执行,不会执行到中间被打断,也不会在其它处理器上并行做这个操作。

Mutex用 pthread_mutex_t 类型的变量表示,可以通过下面的方式初始化和销毁:

#include <pthread.h>
int pthread_mutex_destroy(pthread_mutex_t *mutex);
int pthread_mutex_init(pthread_mutex_t *restrict mutex,
const pthread_mutexattr_t *restrict attr);
pthread_mutex_t mutex = PTHREAD_MUTEX_INITIALIZER;

返回值:成功返回0,失败返回错误号。
pthread_mutex_init 函数对Mutex做初始化,参数 attr 设定Mutex的属性,如果 attr 为 NULL 则表示缺省属性

用 pthread_mutex_init 函数初始化的Mutex可以用 pthread_mutex_destroy 销毁。如果Mutex变量是静态分配的(全局变量或 static 变量),也可以用宏定义 PTHREAD_MUTEX_INITIALIZER 来初始化,相当于用 pthread_mutex_init 初始化并且 attr 参数为 NULL 。Mutex的加锁和解锁操作可以用下列函数

#include <pthread.h>
int pthread_mutex_lock(pthread_mutex_t *mutex);
int pthread_mutex_trylock(pthread_mutex_t *mutex);
int pthread_mutex_unlock(pthread_mutex_t *mutex);

一个线程可以调用pthread_mutex_lock获得Mutex,如果这时另一个线程已经调用pthread_mutex_lock获得了该Mutex,则当前线程需要挂起等待,直到另一个线程调用pthread_mutex_unlock释放Mutex,当前线程被唤醒,才能获得该Mutex并继续执行。如果一个线程既想获得锁,又不想挂起等待,可以调用pthread_mutex_trylock,如果Mutex已经
被另一个线程获得,这个函数会失败返回EBUSY,而不会使线程挂起等待。

可以使用mutex尝试一个例子:

#include<stdio.h>
#include<stdlib.h>
#include<pthread.h>
#define NLOOP 5000
int counter;
pthread_mutex_t counter_mutex = PTHREAD_MUTEX_INITIALIZER;
void *doit(void *);
int main()
{
    pthread_t tidA, tidB;
    pthread_create(&tidA, NULL, doit, NULL);
    pthread_create(&tidB, NULL, doit, NULL);
    /* wait for both threads to terminate */
    pthread_join(tidA, NULL);
    pthread_join(tidB, NULL);
    return 0;
}
void *doit(void *vptr)
{
    int i, val;
    for(i = 0; i < NLOOP; i++){
        pthread_mutex_lock(&counter_mutex);
        val = counter;
        printf("%x: %d\n",(unsigned int)pthread_self(),
              val + 1);
        counter = val + 1;
        pthread_mutex_unlock(&counter_mutex);
    }
    return NULL;
}


Condition Variable:

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

创建条件变量的api如下所示:

#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);
<span style="font-family: Arial, Helvetica, sans-serif;">pthread_cond_t cond = PTHREAD_COND_INITIALIZER;</span>

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

操作条件变量可以使用下面的函数:

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);

一个例子如下所示:

#include <stdio.h>
#include <stdlib.h>
#include <pthread.h>

struct msg{
    struct msg * next;
    int num;
};

struct msg * head;
pthread_cond_t has_product = PTHREAD_COND_INITIALIZER;
pthread_mutex_t lock = PTHREAD_MUTEX_INITIALIZER;

void * consumer(void * p)
{
    struct msg * 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\n", mp->num);
        free(mp);
        sleep(rand()%5);
    }
}

void * producer(void * p)
{
    struct msg * mp;
    for(;;){
        mp = malloc(sizeof(struct msg));
        mp->num = rand()%1000 + 1;
        printf("Produce %d\n", 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()
{
    pthread_t pid, cid;
    srand(time(NULL));
    pthread_create(&pid, NULL, producer, NULL);
    pthread_create(&cid, NULL, consumer, NULL);
    pthread_join(pid, NULL);
    pthread_join(cid, NULL);
    return 0;
}



Semaphore:

信号量(Semaphore)和Mutex类似,表示可用资源的数量,和Mutex不同的是这个数量可以大于1,POSIX semaphore库函数POSIX semaphore库函数不仅可用于同一进程的线程间同步,也可用于不同进程间的同步。

#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表示信号量用于同一进程的线程间同步。调用sem_wait()可以获得资源,使semaphore的值减1,如果调用sem_wait()时semaphore的值已经是0,则挂起等待。如果不希望挂起等待,可以调用sem_trywait()。调用sem_post()可以释放资源,使semaphore的值加1,同时唤醒挂起等待的线程。
下面可以使用条件变量以及mutex的自己写一个信号量,因为有的系统中是不支持信号量的:

typedef struct  {
    int val;
    pthread_mutex_t mutex;
    pthread_cond_t cond;
}semaphore_t;

void sem_init(semaphore_t * s, int n)
{
    pthread_mutex_init(&(s->mutex), NULL);
    pthread_cond_init(&(s->cond), NULL);
    s->val = n;
}

int  sem_wait(semaphore_t * s)
{
    int rc = 0;
    pthread_mutex_lock(&(s->mutex));
    if(s->val == 0){
        pthread_mutex_unlock(&(s->mutex));
        rc = pthread_cond_wait(&(s->cond), &(s->mutex));
        pthread_mutex_lock(&(s->mutex));
    }
    s->val--;
    pthread_mutex_unlock(&(s->mutex));
    return rc;
}

void sem_post(semaphore_t * s)
{
    pthread_mutex_lock(&(s->mutex));
    s->val++;
    pthread_cond_signal(&(s->cond));
    pthread_mutex_unlock(&(s->mutex));
}


你可能感兴趣的:(关于线程间IPC)