线程同步的实现

互斥锁

        互斥锁的使用就是当有线程访问进程空间中的公共资源时,线程执行“加锁”操作(将资源锁起来),阻止其它线程的访问。访问完成后,该线程(谁锁上的必须由谁来解锁)负责完成“解锁”操作,将资源让给其它线程。当有多个线程同时需要访问同一个公共资源时,谁先上锁成功,这公共资源就归谁使用,只有等他用完了,释放锁了,其他线程才能用

互斥锁的使用流程

(1)定义一个互斥锁

        在<pthread.h>头文件中,有一个专门用来定义互斥锁变量的结构体,叫pthread_mutex_t,我们直接拿来用就是了,使用方法如下

pthread_mutex_t myMutex;

(2)初始化互斥锁

        互斥锁的初始化方法有两种,一种是使用特定的宏,一种是使用专门的初始化函数,一般没啥特殊要求我们就用第一种宏就行,对于调用 malloc() 函数分配动态内存的互斥锁,只能使用第二种方式。

使用宏的方法

pthread_mutex_t myMutex = PTHREAD_MUTEX_INITIALIZER;

使用函数的方法

pthread_mutex_t myMutex;
pthread_mutex_init(&myMutex , NULL);

关于pthread_mutex_init() 函数,他的原型是这样的

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

关于他的参数,mutex 参数表示要初始化的互斥锁;attr 参数用于自定义新建互斥锁的属性,当attr 的值为 NULL 时表示以默认属性创建互斥锁,我们一般也就使用NULL。

(3)加锁

int pthread_mutex_lock(pthread_mutex_t* mutex);  

//或者用下面这个

int pthread_mutex_trylock(pthread_mutex_t* mutex); 

有关两者的区别,具体如下

pthread_mutex_lock() 函数会使线程进入等待(阻塞)状态,直至互斥锁得到释放;

pthread_mutex_trylock() 函数不会阻塞线程,直接返回非零数(表示加锁失败)。

(4)解锁

int pthread_mutex_unlock(pthread_mutex_t* mutex); 

说到最后,用一个实际案例来实验一下,模拟的是网上买票的工作流程。创建了四个子线程,去模拟不同的买票窗口去负责卖票。

#include<stdio.h>
#include<stdlib.h>
#include<pthread.h>
#include<unistd.h>
int ticket_sum = 30;//这表示票的总量

//创建互斥锁变量并初始化
pthread_mutex_t myMutex = PTHREAD_MUTEX_INITIALIZER;

//模拟售票员卖票
void *sell_ticket(void *arg) 
{
    //输出当前线程的ID
    printf("id = %u\n", pthread_self());
    int i;
    int islock = 0;
    for (i = 0; i < 10; i++)
    {
        //当前线程“加锁”
        islock = pthread_mutex_lock(&myMutex);
        //如果“加锁”成功,执行如下代码
        if (islock == 0)
        {
            //如果票数 >0 ,开始卖票
            if (ticket_sum > 0)
            {
                sleep(1);
				printf("%u sell the NO.%d ticket\n",pthread_self(),30-ticket_sum+1);
                ticket_sum--;
            }
           	pthread_mutex_unlock(&myMutex);
        }
       
    }
    return 0;
}
int main() 
{
    int flag;
    int i;
    //创建4个线程
    pthread_t tids[4];
    for (i = 0; i < 4; i++)
    {
        flag = pthread_create(&tids[i], NULL, &sell_ticket, NULL);
        if (flag != 0) 
        {
            printf("fail\n");
            return 0;
        }
    }
    sleep(30);   //等待4个子线程完成
    for (i = 0; i < 4; i++)
    {
        flag = pthread_join(tids[i], NULL);
        if (flag != 0) 
        {
            printf("tid=%d wait fail !", tids[i]);
            return 0;
        }
    }
    return 0;
}

        程序中pthread_self()这个其实是这样子的,我直接man出来,大家看一下,就是用来返回,调用线程的id的。

        关于这个程序案例,写完后,我自己其实运行了几次,之前我将票的总数量设置成10,发现从头到尾只有一个线程在被调用,我以为是我的程序有问题,在网上查阅了一下资料发现,是我们当前的电脑机子性能太好所致,当操作数量少的情况下的多线程,电脑自己其实就使用一个线程就给我们安排了,于是我加大了数量,这才发生点变化出来

信号量

        互斥锁的状态值只有开锁和解锁两个方面,信号量则不同,他可以根据实际场景的不同设置自己想要的转态值,可以说是更加灵活吧。

        信号量可以分成是二进制信号量和计数信号量,二进制信号量就可以用来替代互斥锁,就是只将信号量的初始值设置成1,然后只能0,1间变换,可以理解成每次只允许一个线程同时访问一片公共资源,而计数信号量就是将信号量的初始值设置的多一点,大于1的那种,意味着每次可以同时接待多个线程来访问公共资源。

下面来看看信号量这东西的用法,具体咋用

sem_t  mySem;        //先定义一个信号量

//信号量的初始化

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

各个参数的含义分别为:

1、参数sem:表示要初始化的目标信号量;

2、参数pshared:表示该信号量是否可以和其他进程共享,pshared 值为 0 时表示不共享,值为 1 时表示共享;

3、参数value:设置信号量的初始值

对于初始化好了的信号量,我们可以借助 <semaphore.h> 头文件提供的一些函数操作

入参都是要操作的信号量

//将信号量的值“加 1”

int sem_post(sem_t* sem);        

//对信号量做“减 1”操作,当信号量的值为 0 时,阻塞当前线程等待

int sem_wait(sem_t* sem); 

//当信号量的值为 0 时,sem_trywait() 函数并不会阻塞当前线程,而是立即返回 -1;

int sem_trywait(sem_t* sem);

//手动销毁信号量

int sem_destroy(sem_t* sem);

当一个线程要用公共资源的时候就先,sem_wait,等待处理完后再sem_post,把位置让出来,就好比银行里的多个服务窗口,你坐那办理业务的时候就需要把可用位置减1,也就是sem_wait,等你办理完后就需要把位置让出来,也就是sem_post。

条件变量

条件变量是线程同步的另一种手段,主要逻辑就是等待和唤醒。条件不满足时,线程等待;条件满足时,线程被(其他线程)唤醒。条件变量一般和互斥量一起使用,因为需要保证多线程互斥地修改条件。条件变量阻塞的是线程,不像互斥锁阻塞的是资源。

读写锁

读写锁的核心思想是:将线程访问共享数据时发出的请求分为两种,分别是:

读请求:只读取共享数据,不做任何修改;

写请求:存在修改共享数据的行为。

当前读写锁的状态发起读请求发起写请求
无锁允许允许
读锁允许阻塞等待
写锁阻塞等待阻塞等待

其实核心的要点就是,多个读的进程由于并不改变原内容,所以可以多个并行,但一旦设计到写,那就需要等待一下,让写完了,数据内容不变了再进行操作。


如何避免死锁 ?

产生死锁的原因:当进程空间中的某公共资源不允许多个线程同时访问时,某线程访问公共资源后不及时释放资源,就很可能产生线程死锁。

解决办法

使用互斥锁的时候,用完后及时地解锁;使用信号量时,用完后及时 sem_post();读写锁也是一样,及时解锁。

另外,多个线程请求资源的顺序最好保持一致。线程 t1 需要先请求 mutex 锁然后再请求 mutex2 锁,而 t2 需要先请求 mutex2 锁然后再请求 mutex 锁,这是一种典型的因“请求资源顺序不一致”,两个线程之间会相互等待对方解锁而发生死锁。

  • 2
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

翔在天上飞

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值