当前位置:   article > 正文

Linux——线程_2(线程同步)笔记_头歌linux之线程同步二

头歌linux之线程同步二


线程同步

同步即协同步调,按预定的先后顺序执行

  由于线程共享进程的资源和地址空间,当多个线程对同一共享资源进行操作时,为防止数据混乱,因此在对这些资源进行操作时,必须考虑到线程间资源访问的同步和互斥的问题。这里介绍 POSIX 中线程同步的方法,主要有互斥锁、读写锁、条件变量和信号量的方式。互斥锁更适合用于同时可用的资源是唯一的情况;信号量更适合用于同时可用的资源为多个情况

数据混乱的原因

  1. 资源共享(独享资源则不会)
  2. 调度随机(意味着数据访问会出现竞争)
  3. 线程间缺乏必要的同步机制

一、mutex互斥锁

  mutex 是一种简单的加锁的方法来控制对共享资源的存取。这个互斥锁只有两种状态,也就是上锁和解锁,可以把互斥锁看作某种意义上的全局变量。在同一时刻只能有一个线程掌握某个互斥上的锁,拥有上锁状态的线程能够对共享资源进行操作。若其他线程希望对共享数据操作就需要先拿到互斥锁,若该锁已经被另一个线程拥有,则该线程就会挂起,直到上锁的线程释放掉互斥锁为止。

  注意:互斥锁为建议锁,即建议程序中的多线程访问共享数据的时候使用该机制,但并没有强制限定,所以既是有了mutex互斥锁,若线程不使用该机制规则就访问数据,依然会造成数据混乱。

1.互斥锁的操作主要包括以下几个步骤

  • 互斥锁初始化:pthread_mutex_init
  • 互斥锁上锁:pthread_mutex_lock
  • 互斥锁判断上锁:pthread_mutex_trylock
  • 互斥锁解锁:pthread_mutex_unlock
  • 消除互斥锁:pthread_mutex_destroy

互斥锁可以分为快速互斥锁、递归互斥锁和检错互斥锁。这三种锁的区别主要在于其他未占有互斥锁的线程在希望得到互斥锁时的是否需要阻塞等待。

  • 快速锁是指调用线程会阻塞直至拥有互斥锁的线程解锁为止。
  • 递归互斥锁能够成功地返回并且增加调用线程在互斥上加锁的次数,
  • 检错互斥锁则为快速互斥锁的非阻塞版本,它会立即返回并返回一个错误信息。

当pthread_mutex_init函数第二个参数设为NULL时,默认属性为:快速互斥锁

注意:在访问共享资源前加锁,访问结束后立即解锁

2.函数格式

在这里插入图片描述

int pthread_mutex_lock(pthread_mutex_t *mutex)
int pthread_mutex_trylock(pthread_mutex_t *mutex)
int pthread_mutex_unlock(pthread_mutex_t *mutex)
int pthread_mutex_destroy(pthread_mutex_t *mutex)
  • 1
  • 2
  • 3
  • 4

以上函数均是。成功:返回1; 失败:返回错误码

3.死锁

1.线程对同一互斥锁加锁两次
2.线程1拥有A锁,同时请求获得B锁

二、读写锁

读写锁与互斥锁相识,但读写锁拥有更高的并行性,其特征为:写独占,读共享,写锁优先级高

1.读写锁状态

读写锁一一把锁,但是有三种状态

  • 1.读模式下加锁状态(读锁)
  • 2.写模式下加锁状态(写锁)
  • 3.不加锁状态

2.读入数据

	int pthread_rwlock_init(pthread_rwlock_t *restrict rwlock, const pthread_rwlockattr_t *restrict attr); //初始化
	int pthread_rwlock_destroy(pthread_rwlock_t *rwlock);
	int pthread_rwlock_rdlock(pthread_rwlock_t *rwlock);         //设置读锁
    int pthread_rwlock_tryrdlock(pthread_rwlock_t *rwlock);       //尝试获取读出锁
	int pthread_rwlock_wrlock(pthread_rwlock_t *rwlock);          //设置写锁
	int pthread_rwlock_unlock(pthread_rwlock_t *rwlock);          //设置解锁
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6

pthread_rwlock_t 类型,用于定义一个读写锁变量

第二个参数:attr表示读写锁的属性,通常使用默认属性,传NULL即可

以上函数,成功:返回0,失败:返回错误号


三、条件变量

条件变量本身不是锁,但是它可以造成线程阻塞,通常与互斥锁配合使用,给多线程提供一个回合场所

1.相应函数

int pthread_cond_init(pthread_cond_t *restrict cond, const pthread_condattr_t *restrict attr);  //初始化条件变量
int pthread_cond_destroy(pthread_cond_t *cond);
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_signal(pthread_cond_t *cond);       //发出信息唤醒某个阻塞线程
int pthread_cond_broadcast(pthread_cond_t *cond);    //唤醒所有阻塞线程
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7

pthread_cond_t类型,用于定义一个条件变量

第二个参数:attr表示条件变量的属性,通常使用默认属性,传NULL即可

以上函数,成功:返回0,失败:返回错误号

2.生产者消费者模型

三、信号量

信号量是互斥锁的进化版。即可应用于线程也可应用于进程

信号量,是一种相对折中的处理方式,既能保证同步,数据不混乱,又能提高线程并发

1.相应函数

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_timedwait(sem_t *sem, const struct timespec *abs_timeout);

int sem_post(sem_t *sem);//它将信号量的值加一同时发出信号唤醒等待的进程。

int sem_getvalue(sem_t *sem, int *sval);//由于得到信号量的值

int sem_destroy(sem_t *sem);//用于删除信号量
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12

sem_t类型,定义信号量

pshared:
  值为0,线程间同步
  值为非0,进程间同步

value:信号量初始化值,它的大小决定了占用信号量的线程的个数

返回值:成功:返回0,失败:返回-1

2.生产者消费者模型

声明:本文内容由网友自发贡献,不代表【wpsshop博客】立场,版权归原作者所有,本站不承担相应法律责任。如您发现有侵权的内容,请联系我们。转载请注明出处:https://www.wpsshop.cn/w/盐析白兔/article/detail/336596
推荐阅读
相关标签
  

闽ICP备14008679号