Linux多线程间通信机制(互斥锁、条件变量、信号量,读写锁)同步与异步(一)

转载 https://www.cnblogs.com/wanghuaijun/p/9532604.html

概要:多线程通信的方式,同步和异步
**同步:**互斥锁,条件变量,读写锁,信号量。
**异步:**信号

一. 同步机制
线程间的同步机制主要包括三个:

互斥锁:
以排他的方式,防止共享资源被并发访问;互斥锁为二元变量, 状态为0-开锁、1-上锁;开锁必须由上锁的线程执行,不受其它线程干扰.
条件变量:
满足某个特定条件时,可通过条件变量通知其它线程do-something;必须与互斥锁*联合使用,单独无法执行.
读写锁:
针对多读者,少写者的情况设定,允许多读,但此时不可写;唯一写,此时不可读.

函数的头文件为:> #include <phtread.h>

1. 互斥锁
操作流程:
I. 创建互斥锁。II. 申请锁:若可用,立刻占用;否则,阻塞等待。III. do-something。IV. 释放锁。V. 销毁锁。
以下是互斥锁的基本操作函数:功能 函数 参数 返回值 说明:

1、初始化锁

int pthread_mutex_init(pthread_mutex_t *mutex,const pthread_mutexattr_t *attr)

mutex: 欲建立的互斥锁
attr:属性,一般为NULL
成功:0,失败:非零值

2、阻塞申请锁

int pthread_mutex_lock(pthread_mutex_t *mutex)

mutex:互斥锁
成功:0,失败:非零值 ,若未申请到,阻塞等待

3、非阻塞申请

int pthread_mutex_trylock(pthread_mutex_t *mutex)

mutex:互斥锁
成功:0,失败:非零值 ,若未申请到,返回错误
4、释放锁

int pthread_mutex_unlock(pthread_mutex_t *mutex)

mutex:互斥锁
成功:0,失败:非零值

5、销毁锁

int pthread_mutex_destroy(pthread_mutex_t *mutex)

mutex:互斥锁
成功:0,失败:非零值

2. 条件变量
注意,条件变量必须与互斥锁共同使用;
以下是条件变量的基本操作函数:功能 函数 参数 返回值 说明

1、初始化锁

int pthread_cond_init(pthread_cond_t *cond,const pthread_condattr_t *attr)

cond: 欲建立的条件变量
attr:属性,一般为NULL
成功:0,失败:非零值

2、等待条件变量

int pthread_cond_wait(pthread_cond_t *cond,pthread_mutex_t *mutex)

cond:条件变量
mutex:互斥锁
成功:0,失败:非零值 阻塞等待,隐含释放申请到的互斥锁

3、限时等待条件变量

int pthread_cond_timewait(pthread_cond_t *cond,pthread_mutex_t *mutex,const struct timespec *time)

time:等待过期的绝对时间
从1970-1-1:0:0:0起
成功:0,失败:非零值

struct timespec{long ts_sec;long ts_nsec}

4、单一信号通知

int pthread_cond_signal(pthread_cond_t *cond)

cond:条件变量
成功:0,失败:非零值 唤醒等待cond的第一个线程,隐含获取需要的互斥锁

5、广播信号通知

int pthread_cond_broadcast(pthread_cond_t *cond)

cond:条件变量
成功:0,失败:非零值, 唤醒所有等待cond的线程 ,隐含获取需要的互斥锁

6、销毁条件变量

int pthread_cond_destroy(pthread_cond_t *cond)

cond:条件变量
成功:0,失败:非零值

3. 读写锁
读写基本原则:
若当前线程读数据,则允许其他线程读数据,但不允许写,若当前线程写数据,则不允许其他线程读、写数据
以下是基本的操作:功能 函数 参数 返回值 说明:

1、初始化锁

int pthread_rwlock_init(pthread_rwlock_t *rwlock,const pthread_rwlockattr_t *attr)

rwlock: 欲建立的读写锁
attr:属性,一般为NULL
成功:0失败:非零值

2、阻塞申请读锁

int pthread_rwlock_rdlock(pthread_rwlock_t *rwlock)

rwlock:读写锁
成功:0,失败:非零值 若未申请到,阻塞等待

3、非阻塞申请

int pthread_rwlock_tryrdlock(pthread_rwlock_t *rwlock)

rwlock:读写锁
成功:0,失败:非零值 若未申请到,返回错误

4、阻塞申请写锁

int pthread_rwlock_wrlock(pthread_rwlock_t *rwlock)

rwlock:读写锁
成功:0,失败:非零值 若未申请到,阻塞等待

5、非阻塞申请写锁

int pthread_rwlock_trywrlock(pthread_rwlock_t *rwlock)

rwlock:读写锁 成功:0,失败:非零值 若未申请到,返回错误

6、释放锁

int pthread_mutex_unlock(pthread_rwlock_t *rwlock)

rwlock:读写锁 成功:0,失败:非零值

7、销毁锁

int pthread_rwlock_destroy(pthread_rwlock_t *rwlock)

rwlock:读写锁 成功:0,失败:非零值

4. 线程信号量
线程信号量类似进程的信号量,主要是使得多个线程访问共享资源时,顺序互斥访问。
与互斥锁的区别在于:互斥锁:只有一个bool类型的值,只允许2个线程进行排队;信号量:允许多个线程共同等待一个共享资源

函数如下: #include <semaphore.h>
功能 函数 参数 返回值 说明:

1、创建信号量

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

sem:信号量地址; pshared:是(!=0)否(0)为共享信号量,value:信号量初值
0: 成功,-1: 失败

2、P操作(阻塞)

int sem_wait(sem_t *sem)

sem:信号量地址
0: 成功,-1: 失败

3、P操作(非阻塞)

int sem_trywait(sem_t *sem)

sem:信号量地址
0: 成功,-1: 失败

4、P操作(时间)

int sem_timedwait(sem_t *sem,const struct timespec *abs_timeout)

sem:信号量地址,abs_timeout:超时时间
0: 成功,-1: 失败
struct timespec 见下面

5、V操作

int sem_post(sem_t *sem)

sem:信号量地址
0: 成功,-1: 失败

6、获取信号量值

int sem_getvalue(sem_t *sem, int *sval)

sem:信号量地址,sval: 将信号量值放到该地址
0: 成功,-1: 失败

7、删除信号量

int sem_destroy(sem_t *sem)

sem:信号量地址
0: 成功,-1: 失败
struct timespec {
time_t tv_sec; /* Seconds /
long tv_nsec; /
Nanoseconds [0 … 999999999] */
};

二. 异步机制 - 信号
线程的异步机制只有信号,类似于线程的信号。
线程信号具备以下特点:
任何线程都可以向其它线程(同一进程下)发送信号;每个线程都具备自己独立的信号屏蔽集,不影响其它线程;线程创建时,不继承原线程的信号屏蔽集;同进程下,所有线程共享对某信号的处理方式,即一个设置,所有有效;多个线程的程序,向某一个线程发送终止信号,则整个进程终止。

信号的基本操作如下:功能 函数 参数 返回值 说明

1、安装信号

sighandler_t signal( int signum, sighandler_t handler)

signum:信号值,handler:信号操作  
详情参见:http://www.cnblogs.com/Jimmy1988/p/7575103.html

2、发送信号

int pthread_kill( pthread_t threadid,int signo)

threadid: 目标线程id,signo:信号值
成功:0,失败:非零值

若signo=0,检测该线程是否存在

3、不发送信号设置屏蔽集

pthread_sigmask(int how,const sigset_t *set, sigset_t *oldset)

how:如何更改信号掩码,newmask:新的信号屏蔽集,原信号屏蔽集
成功:0,失败:非零值

how值:
 1.SIG_BLOCK:添加新掩码
 2.SIG_UNBLOCK:删除新掩码
 3.SIG_SETMASK:设置新掩码完全替换旧值

也可以参考这篇博客:https://www.cnblogs.com/coding-my-life/p/4782529.html

三、示例代码,同步机制:

1). 互斥锁:

两个线程:
读线程:从stdin中读取数据,并存储。
写线程:从存储buffer中读取数据并显示。

#include <stdio.h>
#include <string.h>
#include <pthread.h>
#include <unistd.h>
#include <stdlib.h>
#define SIZE 128
pthread_mutex_t mutex;
int EXIT = 0;
char word[SIZE];
void * child(void *arg)
{
    while(1)
    {
        while(strlen(word) == 0)
            usleep(100);

        pthread_mutex_lock(&mutex);
        printf("The input words: %s\n", word);
        pthread_mutex_unlock(&mutex);
        if(strcmp("end\n", word) == 0)
        {
            printf("The process end\n");
            EXIT = 1;
            break;
        }
        memset(word, '\0', SIZE);
    }
    return ;
}
int main()
{
    //1. create the lock
    pthread_mutex_init(&mutex, NULL);
    
    //2.create a new thread
    pthread_t tid;
    pthread_create(&tid, NULL, (void *)*child, NULL);
    
    //3. Input words
    while(EXIT == 0)
    {
        if(strlen(word)!=0)
            usleep(100);
        //add the lock
        else
        {
            pthread_mutex_lock(&mutex);
            printf("Input words:  ");
            fgets(word, SIZE, stdin);
            pthread_mutex_unlock(&mutex);            
        }
    }

    pthread_join(tid, NULL);
    printf("The child has joined\n");
    pthread_mutex_destroy(&mutex);

    return 0;
}

2). 条件变量:
生产者和消费者问题:

生产者:
向仓库生产数据(大小可任意设定),当满时,阻塞等待仓库有空闲(由消费者消费完后通知)
消费者:
从仓库读数据,若仓库为空,则阻塞等待,当生产者再次生产产品后通知

#include <stdio.h>
#include <string.h>
#include <stdlib.h>
#include <unistd.h>
#include <pthread.h>
#define SIZE 2
int Data[SIZE];
typedef struct
{
    pthread_mutex_t lock;
    pthread_cond_t  notFull;
    pthread_cond_t  notEmpty;
    int read_point;
    int write_point;
}sCOND;
sCOND *pCondLock;
void init(void)
{
    //memset(pCondLock, 0, sizeof(sCOND));

    //1.Create a mutex lock
    pthread_mutex_init(&pCondLock->lock, NULL);

    //2.Create two condition variable  
    pthread_cond_init(&pCondLock->notFull, NULL);
    pthread_cond_init(&pCondLock->notEmpty, NULL);

    //set the read and write point 0
    pCondLock->read_point = 0;
    pCondLock->write_point = 0;
}
int put(int data) 
{
    //obtain the mutex lock
    pthread_mutex_lock(&pCondLock->lock);
    
    //check the global variable Data full or not
    while((pCondLock->write_point+1)%SIZE == pCondLock->read_point)
    {
        printf("The buf is full, waitting for not_full signal\n");
        pthread_cond_wait(&pCondLock->notFull, &pCondLock->lock);
    }       

    //write the data to buffer
    Data[pCondLock->write_point] = data;
    pCondLock->write_point++;
    if(pCondLock->write_point == SIZE)
        pCondLock->write_point = 0;
    
    //unlock the mutex lock 
    pthread_mutex_unlock(&pCondLock->lock);

    //wake up the not_empty signal
    pthread_cond_signal(&pCondLock->notEmpty);
     
    return 0;
}
int get(int *data) 
{
    //obtain the mutex lock
    pthread_mutex_lock(&pCondLock->lock);
    
    //check the global variable Data empty or not
    while(pCondLock->write_point == pCondLock->read_point)
    {
        printf("The buf is empty, waitting for not_empty signal\n");
        pthread_cond_wait(&pCondLock->notEmpty, &pCondLock->lock);
    }       

    //read the data from buffer
    *data = Data[pCondLock->read_point];
    pCondLock->read_point++;
    if(pCondLock->read_point == SIZE)
        pCondLock->read_point = 0;
    
    //wake up the not_empty signal
    pthread_cond_signal(&pCondLock->notFull);
    pthread_mutex_unlock(&pCondLock->lock);
    return *data;
}
void *produce(void)
{
    int times=0;
    //1. first 5 times, every second write a data to buffer
    for(times=0; times < 5; times++)
    {
        sleep(1);
        put(times+1);
        printf("Input date=%d\n", times+1);
    }

    //2. last 5 times, every 3 seconds write a data to buffer 
    for(times = 5; times < 10; times++)
    {
        sleep(3);
        put(times+1);
        printf("Input date=%d\n", times+1);
    }
}
void *consume(void)
{
    int times=0;
    int data=0;
    //10 times, every 2 seconds read the buffer
    for(times = 0; times < 10; times++)
    {
        sleep(2);
        data = get(&data);
        printf("The data is %d\n", data);
    }
}
int main()
{
    pthread_t tid1, tid2;
    
    pCondLock = malloc(sizeof(sCOND));
    memset(pCondLock, '\0', sizeof(sCOND));
    //1.init the struct of sCondLock 
    init();

    //2. start two threads
    pthread_create(&tid1, NULL, (void*)*produce, NULL);
    pthread_create(&tid2, NULL, (void*)*consume, NULL);
    
    pthread_join(tid1, NULL);
    pthread_join(tid2, NULL);

    free(pCondLock);
    return 0;
}

3). 读写锁:

四个线程:两读两写;
多线程可同时读,但此时不可写;
只有一个线程可写,其它线程等待该线程写完后执行响应的读/写操作

#include <stdio.h>
#include <unistd.h>
#include <string.h>
#include <pthread.h>
#include <stdlib.h>
#define BUF_SIZE 128

char buf[BUF_SIZE];
pthread_rwlock_t rwlock;

int time_to_exit = 0;
void *read_first(void *arg);
void *read_second(void *arg);
void *write_first(void *arg);
void *write_second(void *arg);
int main()
{
    pthread_t tid_rd1, tid_rd2;
    pthread_t tid_wr1, tid_wr2;

    //1.create a read-write-lock
    int ret = pthread_rwlock_init(&rwlock, NULL);
    if(ret != 0)
    {
        perror("pthread_rwlock_init");
        exit(EXIT_FAILURE);
    }

    //2. Create the read and write threads
    ret = pthread_create(&tid_rd1, NULL, (void *)*read_first, NULL);
    if(ret != 0)
    {
        perror("pthread_create");
        exit(EXIT_FAILURE);
    }

    ret = pthread_create(&tid_rd2, NULL, (void *)*read_second, NULL);
    if(ret != 0)
    {
        perror("pthread_create");
        exit(EXIT_FAILURE);
    }

    ret = pthread_create(&tid_wr1, NULL, (void *)*write_first, NULL);
    if(ret != 0)
    {
        perror("pthread_create");
        exit(EXIT_FAILURE);
    }

    ret = pthread_create(&tid_wr2, NULL, (void *)*write_second, NULL);
    if(ret != 0)
    {
        perror("pthread_create");
        exit(EXIT_FAILURE);
    }


    //3. wait for the threads finish
    pthread_join(tid_rd1, NULL);
    pthread_join(tid_rd2, NULL);
    pthread_join(tid_wr1, NULL);
    pthread_join(tid_wr2, NULL);

    //4. delete the read-write-lock
    pthread_rwlock_destroy(&rwlock);

    return 0;
}

/***************************************************/

// 1,Write threads
void *write_first(void *arg)
{
    while(!time_to_exit)
    {
        sleep(5);
        
        //1. get the read-lock    
        pthread_rwlock_wrlock(&rwlock);
        printf("\nThis is thread write_first!\n");

        printf("Pls input the string: ");
        fgets(buf, BUF_SIZE, stdin);

        pthread_rwlock_unlock(&rwlock);
    }

    printf("Exit the write_first!\n");
    pthread_exit(0);
}
void *write_second(void *arg)
{
    while(!time_to_exit)
    {
        sleep(10);
        
        //1. get the read-lock    
        pthread_rwlock_wrlock(&rwlock);
        printf("\nThis is thread write_second!\n");

        printf("Pls input the string: ");
        fgets(buf, BUF_SIZE, stdin);

        pthread_rwlock_unlock(&rwlock);
    }

    printf("Exit the write_second!\n");
    pthread_exit(0);
}

/*********************************************/

//2. read the threads
void *read_first(void *arg)
{
    while(1)
    {
        sleep(5);
        pthread_rwlock_rdlock(&rwlock);
        printf("\nThis is thread read_first\n");

        //if write an string of "end"
        if(!strncmp("end", buf, 3))
        {
            printf("Exit the read_first!\n");
            break;
        }
        
        //if nothing in the BUFFER
        while(strlen(buf) == 0)
        {
            pthread_rwlock_unlock(&rwlock);
            sleep(2);
            pthread_rwlock_rdlock(&rwlock);
        }
        
        //output the string in BUFFER
        printf("The string is: %s\n", buf);

        pthread_rwlock_unlock(&rwlock);
    }

    pthread_rwlock_unlock(&rwlock);

    //make the exit true
    time_to_exit = 1;

    pthread_exit(0);
}
void *read_second(void *arg)
{
    while(1)
    {
        sleep(4);

        pthread_rwlock_rdlock(&rwlock);
        printf("\nThis is thread read_second\n");

        //if write an string of "end"
        if(!strncmp("end", buf, 3))
        {
            printf("Exit the read_second!\n");
            break;
        }
        
        //if nothing in the BUFFER
        while(strlen(buf) == 0)
        {
            pthread_rwlock_unlock(&rwlock);
            sleep(2);
            pthread_rwlock_rdlock(&rwlock);
        }
        
        //output the string in BUFFER
        printf("The string is: %s\n", buf);

        pthread_rwlock_unlock(&rwlock);
    }

    pthread_rwlock_unlock(&rwlock);

    //make the exit true
    time_to_exit = 1;

    pthread_exit(0);
}

四、示例代码, 异步机制
1)- 信号:

本程序包括两个线程:
线程1安装SIGUSR1,阻塞除SIGUSR2外的所有信号;
线程2安装SIGUSR2,不阻塞任何信号

操作流程:
1- 线程1、2安装信号;
2- 主线程发送SIGUSR1和SIGUSR2至线程1和线程2;
3- 线程1接收到除SIGUSR2之外的信号,阻塞不执行;当收到SIGUSR2后,执行对应操作;
4- 线程2接收到SIGUSR1和SIGUSR2后,分别执行对应操作
5- 主线程发送SIGKILL信号,结束整个进程

#include <stdio.h>
#include <string.h>
#include <unistd.h>
#include <pthread.h>
#include <signal.h>
#include <stdlib.h>
void *th_first(void *arg);

void *th_second(void *arg);

pthread_t tid1, tid2;
void handler(int signo)
{
    printf("In handler: tid_%s, signo=%d\n", ((pthread_self() == tid1)?"first":"second"), signo);
}
int main()
{
    int ret = 0;
    //1. create first thread
    ret = pthread_create(&tid1, NULL, (void *)*th_first, NULL);
    if(0 !=ret)
    {
        perror("pthread_create");
        exit(EXIT_FAILURE);
    }
    //2. create second thread
    ret = pthread_create(&tid2, NULL, (void *)*th_second, NULL);
    if(0 !=ret)
    {
        perror("pthread_create");
        exit(EXIT_FAILURE);
    }

    sleep(2);
    //3. send the signal of SIG_USER1 and SIG_USER2 to thread_first
    ret = pthread_kill(tid1, SIGUSR1);
    if(0 !=ret)
    {
        perror("pthread_kill");
        exit(EXIT_FAILURE);
    }

    ret = pthread_kill(tid1, SIGUSR2);
    if(0 !=ret)
    {
        perror("pthread_kill");
        exit(EXIT_FAILURE);
    }

    //4. send the signal of SIG_USER1 and SIG_USER2 to thread_second_
    sleep(1);
    ret = pthread_kill(tid2, SIGUSR1);
    if(0 !=ret)
    {
        perror("pthread_kill");
        exit(EXIT_FAILURE);
    }

    ret = pthread_kill(tid2, SIGUSR2);
    if(0 !=ret)
    {
        perror("pthread_kill");
        exit(EXIT_FAILURE);
    }

    sleep(1);
    //5. send SIGKILL to all threads
    ret = pthread_kill(tid1, SIGKILL);
    if(0 !=ret)
    {
        perror("pthread_kill");
        exit(EXIT_FAILURE);
    }

    pthread_join(tid1, NULL);
    pthread_join(tid2, NULL);

    return 0;
}
void *th_first(void *arg)
{
    //1. Add SIGUSR1 signal
    signal(SIGUSR1, handler);
    
    //2. Set the sinagl set
    sigset_t set;
    sigfillset(&set);                           //init set to be full, include all signal
    sigdelset(&set, SIGUSR2);                   //delete the SIGUSR2 from the set variable
    pthread_sigmask(SIG_SETMASK, &set, NULL);   //set the current mask set to be defined set variable
    
    //3. Circular wait the signal
    int i;
    for(i=0; i<5; i++)
    {
        printf("\nThis is th_first, tid=%#x\n ", pthread_self());
        pause();
    }
}
void *th_second(void *arg)
{
    usleep(100);
    //1. Add the signal of SIGUSR2
    signal(SIGUSR2, handler);

    //2. Circular wait the signal
    int i;
    for(i=0; i<5; i++)
    {
        printf("\nThis is th_second, tid=%#x\n", pthread_self());
        pause();
    }
}
  • 1
    点赞
  • 2
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值