系统编程六(多线程同步)

1.信号量

操作函数
创建int sem_init(sem_t *sem, int pshared, unsigned int value)
销毁int sem_destroy(sem_t *sem)
阻塞等待int sem_wait(sem_t *sem)
非阻塞等待int sem_trywait(sem_t * sem)
触发int sem_post(sem_t *sem)

1.1 创建

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

参数说明:

参数含义
sem信号量对象
pshared信号量类型。0:线程共享;<0:进程共享
value初始值

返回值说明:

返回值含义
0成功
-1失败

1.2 销毁

int sem_destroy(sem_t *sem);

参数说明:

参数含义
sem信号量对象

返回值说明:

返回值含义
0成功
-1失败

1.3 等待

1.3.1 阻塞等待
int sem_wait(sem_t *sem);

参数说明:

参数含义
sem信号量对象

返回值说明:

返回值含义
0成功
-1失败
1.3.2 非阻塞等待
int sem_trywait(sem_t * sem);

参数说明:

参数含义
sem信号量对象

返回值说明:

返回值含义
0成功
-1失败

1.4 触发

int sem_post(sem_t *sem);

参数说明:

参数含义
sem信号量对象

返回值说明:

返回值含义
0成功
-1失败

示例:

#include<iostream>
#include<pthread.h>
#include<unistd.h>
#include<semaphore.h>
using namespace std;

int n = 0;
void Output(sem_t* psem){
        for(int i=0;i<1000;++i){
                sem_wait(psem);
                cout << "func" << pthread_self() << "[" << ++n << "]" <<endl;
                sem_post(psem);
        }
}

int main(){
        sem_t sem;
        sem_init(&sem,0,1);		//参数为0表示为线程信号量。第三个参数为1表示为二值信号量
        cout << "main thread" << pthread_self() << endl;
        pthread_t tid;
        pthread_create(&tid,NULL,reinterpret_cast<void*(*)(void*)>(Output),&sem)   //强制类型转换
;
        cout << "new thread:" << tid << endl;
        Output(&sem);
        pthread_join(tid,NULL);
        sem_destroy(&sem);
        cout << "process exit" << endl;
}

案例:
竞争量:

#include <stdio.h>
#include <pthread.h>
 
void* func(void* arg){
    printf("enter func\n");
    sleep(1);
    printf("do something\n");
    sleep(1);
    printf("level func\n");
}
 
int main(int argc,int argv[]){  
    pthread_t tids[3];
    int i;
    for(i=0;i<3;i++){
        pthread_create(&tids[i],NULL,func,&mutex);
    }
    for(i=0;i<3;i++){
        pthread_join(tids[i],NULL);
    }
}

解决竞争量:

#include <stdio.h>
#include <semaphore.h>
#include <pthread.h>
 
void* func(void* arg){
    sem_wait(arg);
    printf("enter func\n");
    sleep(1);
    printf("do something\n");
    sleep(1);
    printf("level func\n");
    sem_post(arg);
}
 
int main(int argc,int argv[]){
    sem_t sem;
    sem_init(&sem,0,1);
     
    pthread_t tids[3];
    int i;
    for(i=0;i<3;i++){
        pthread_create(&tids[i],NULL,func,&sem);
    }
    for(i=0;i<3;i++){
        pthread_join(tids[i],NULL);
    }
    sem_destroy(&sem);
 
}

2.互斥量

2.1 分类

在这里插入图片描述

2.2 操作

在这里插入图片描述
注意:使用pthread_cleanup,保证线程正常或者异常退出都能释放互斥锁。

2.3 基本套路

在线程处理函数中使用互斥量的基本套路

pthread_cleanup_push(pthread_mutex_unlock,pmutex);
pthread_mutex_lock(pmutex);

// do something

pthread_exit(0); 			// 退出才能触发clean_up
pthread_cleanup_pop(0);

2.4 信号量与互斥量的区别

在这里插入图片描述

3 条件变量

3.1 分类实现

分类实现
静态分配条件变量pthread_cond_t cond = PTHREAD_COND_INITIALIZER;
动态分配静态变量pthread_cond_init(&cond, NULL);pthread_cond_destroy(&cond);

3.2 操作

操作函数
条件等待int pthread_cond_wait(pthread_cond_t *cond, pthread_mutex_t *mutex)
计时等待int pthread_cond_timedwait(pthread_cond_t *cond, pthread_mutex_t *mutex, const struct timespec *abstime)
单个激活int pthread_cond_signal(pthread_cond_t *cond)
全部激活int pthread_cond_broadcast(pthread_cond_t *cond)
3.2.1 等待

条件等待:

int pthread_cond_wait(pthread_cond_t *cond, pthread_mutex_t *mutex);

参数说明:

参数含义
cond条件变量
mutex互斥锁

计时等待:

int pthread_cond_timedwait(pthread_cond_t *cond, pthread_mutex_t *mutex, const struct timespec *abstime)

参数说明:

参数含义
cond条件变量
mutex互斥锁
abstime等待时间

返回值说明:

返回值含义
ETIMEDOUT超时结束等待
3.2.2 激活

单个激活:
参数说明:

参数含义
cond条件变量

返回值说明:

返回值含义
0成功
正数错误码

全部激活:

int pthread_cond_broadcast(pthread_cond_t *cond)

参数说明:

参数含义
cond条件变量

返回值说明:

返回值含义
0成功
正数错误码
3.3 使用惯例

条件变量一般与互斥锁一起使用。

条件变量发送信号:

thread_mutex_lock(&mutex);
// do something
if(判断条件){
    pthread_cond_signal(&cond);// 唤醒单个
    // 或者
    pthread_cond_broadcast(&cond);// 唤醒多个
}
pthread_mutex_unlock(&mutex);

条件变量等待信号:

pthread_mutex_lock(&mutex); 
while(判断条件){
    pthread_cond_wait(&cond,&mutex);
}
// do something
// 把判断条件改为false
pthread_mutex_unlock(&mutex);

在这里插入图片描述

3.4 互斥锁案例

抢票机制:
主线程放票时上锁,放20张票,然后去掉锁,让五个新建的线程行资源抢夺,如果没票的情况下,线程会进入停等状态,等待主线程继续放票出来。如果票被抢光时,线程退出

#include<iostream>
#include<pthread.h>
#include<unistd.h>
#include<stdlib.h>
using namespace std;

pthread_mutex_t mutex = PTHREAD_MUTEX_INITIALIZER;
pthread_cond_t cond = PTHREAD_COND_INITIALIZER;

int num = 0;			//票的数量
int count = 5;			//放票的次数

void* GetTicket(void*){
        while(true){
				//记录资源释放操作
                pthread_cleanup_push((void(*)(void*))pthread_mutex_unlock,&mutex);
                pthread_mutex_lock(&mutex);
                while(num<=0){
                        if(0==num && 0==count){
                                cout << pthread_self() << "exit" << endl;
                                pthread_exit(0);
                                 }
                        pthread_cond_wait(&cond,&mutex);		//两个作用:
                }							//1.不满足条件,进入等待(阻塞),并且自动释放互斥锁  			
				--num;						//2.等待board_cast/single信号,重新抢夺互斥锁		
                cout << pthread_self() << "取走一张票,还剩" << num << "张票" << endl;
                pthread_mutex_unlock(&mutex);
                usleep(100000);
                pthread_cleanup_pop(0); 			//调用pthread_exit()后线程退出自动释放资源
        }
}
int main(){
        num = 100;
        pthread_t tids[5];
        for(int i=0;i<5;++i){
                pthread_create(tids+i,NULL,GetTicket,NULL);
        }
        //主线程放票
        while(count--){
                pthread_mutex_lock(&mutex);
                num+=20;
                cout << "主线程放票20张,现存" << num <<"票" << endl;
                pthread_mutex_unlock(&mutex);
                pthread_cond_broadcast(&cond);			//唤醒所有在wait线程
                //pthread_cond_single(&cond);			//唤醒其中一个在wait的线程
                sleep(1);                      			//模拟耗时操作
        }
        for(int i=0;i<5;++i){
                pthread_join(tids[i],NULL);
        }
}

用c++11来实现:

#include <iostream>
#include <thread>
#include <mutex>
#include <condition_variable>
using namespace std;

int main(){
    int n = 0;
    int count = 5;
    mutex m;
    condition_variable cv;

    thread ts[5];
    for(auto& t:ts){
        t = thread([&]{
            while(true){
                {							//定义锁的作用域
                    unique_lock<mutex> lock(m);
                    cv.wait(lock,[&]{return n>0||0==n&&0==count;});	//条件变量,对应上面
                    if(n==0&&count==0){
                        cout << this_thread::get_id() << " exit" << endl;
                    }
                    cout << this_thread::get_id() << ":get a ticket,remin" << --n << endl;
                }
                this_thread::sleep_for(100ms);			//将延时操作放在锁外
            }
        });
    }
    while (count){
        {
            unique_lock<mutex> lock(m);
            n+=10;
            --count;
            cout << this_thread::get_id() << ":publish 10 tickets,remain" << n << endl;
            cv.notify_all();				//唤醒所有等待线程
//            cv.notify_one();				//唤醒所有等待线程中的一个
        }
        this_thread::sleep_for(1s);
    }
    for(auto& t:ts){
        t.join();
    }
}

4.读写锁

资源访问分为两种情况:读操作和写操作。

读写锁比mutex有更高的适用性,可以多个线程同时占用读模式的读写锁,但是只能一个线程占用写模式的读写锁。

1.当读写锁是写加锁状态时,在这个锁被解锁之前,所有试图对这个锁加锁的线程都会被阻塞;
2.当读写锁在读加锁状态时,所有试图以读模式对它进行加锁的线程都可以得到访问权,但是以写模式对它进行枷锁的线程将阻塞;
3.当读写锁在读模式锁状态时,如果有另外线程试图以写模式加锁,读写锁通常会阻塞随后的读模式锁请求,这样可以避免读模式锁长期占用,而等待的写模式锁请求长期阻塞;

这种锁适用对数据结构进行读的次数比写的次数多的情况下,因为可以进行读锁共享。

共享独占:
读取锁(共享)
写入锁(独占)

4.1分类

分类实现
静态分配条件变量pthread_rwlock_t rwlock = PTHREAD_RWLOCK_INITIALIZER
动态分配静态变量pthread_rwlock_init(&rwlock, NULL);pthread_rwlock_destroy(&rwlock);

4.2 加锁

4.2.1 读取锁
int pthread_rwlock_rdlock(pthread_rwlock_t *rwlock);
int pthread_rwlock_tryrdlock(pthread_rwlock_t *rwlock);
4.2.2 写入锁
int pthread_rwlock_wrlock(pthread_rwlock_t *rwlock);
int pthread_rwlock_trywrlock(pthread_rwlock_t *rwlock);

4.3 解锁

int pthread_rwlock_unlock(pthread_rwlock_t *rwlock)
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值