线程及线程同步

线程

线程是计算机中独立运行的最小单位。对用户来说的线程在linux内核看来就是的进程(轻量级进程),主线程和子线程共享内存空间,但是他们的pcb(内存控制块)是不同的。
在这里插入图片描述

线程被创建之后,地址空间没有发生生变化,主线程与子线程共享地址空间,但有独立的pcb

主线程与子线程不共享栈区,其他的区域共享。所以线程间通信可以使用全局变量,或者使用堆空间进行通信

创建子线程时不复制内存空间,所以线程更节省空间。

1.1 创建线程

int pthread_create(
                   pthread_t *thread, //线程ID = 无符号长整形
                   const pthread_attr_t *attr, //线程属性,NULL
                   void *(*start_routine)(void *), //线程处理函数
                   void *arg  //线程处理函数参数
);
  • 参数:
    • thread:传出参数,线程创建成功之后,会被设置一个合适的值
    • attr:默认传NULL
    • start_routine:子线程的处理函数
    • arg:回调函数的参数
  • 返回值:成功返回0,失败返回错误号,perror()不能使用该函数打印错误信息

1.2 单个线程退出

 #include <pthread.h>

       void pthread_exit(void *retval);
  • retval:必须指向全局或堆

1.3 等待线程退出

#include <pthread.h>

       int pthread_join(pthread_t thread, void **retval);

参数:

  • thread:要回收的子线程的线程ID
  • retval:读取线程退出的时候携带的状态信息

1.4 线程分离

#include <pthread.h>

       int pthread_detach(pthread_t thread);
  • 调用该函数之后不需要pthread_join
  • 子线程会自动回首自己的pcb

1.5 取消线程

#include <pthread.h>

       int pthread_cancel(pthread_t thread);
  • 在要杀死的子线程对应的处理的函数内部,必须做过一次系统调用
    • pthread_testcancel(); //设置取消点

2. 线程同步

不同的线程同事操作共享区域数据可能会出现混乱,为了解决这一问题,就有了线程同步来协同步调,让线程按照一定的顺序执行

线程同步要用到锁,条件变量。下面进行介绍。

2.1 互斥锁(互斥量)

在说锁之前,先说一下什么是原子操作。原子操作可以看做是CPU处理一个指令,在处理一个指令的时候,不会让别的指令执行。

1.互斥锁类型:
创建一把锁(定义互斥锁,全局变量):pthread_mutex_t mutex;

2.互斥锁的特点:

  • 多个线程访问共享数据的时候是串行的

3.使用互斥锁缺点

  • 串行,效率低

4.互斥锁使用的步骤

  • 1.创建互斥锁:pthread_mutex_t mutex;
  • 2.初始化:phtread_mutex_lock_init(&mutex, NULL); - - mutex = 1
  • 找到线程共同操作的共享数据
    • 3.加锁(操作共享资源之前加锁):
      • pthread_mutex_lock(&mutex); //阻塞线程 - - mutex = 0
      • pthread_mutex_trylock(&mutex); //如果锁上锁,直接返回,不阻塞,共享数据操作
    • 4.解锁:pthread_mutex_unlock(&mutex); - - mutex = 1
      • 阻塞在锁上的线程全部被唤醒
  • 5.销毁:pthread_mutex_dedtroy(&mutex);

如果要加锁,对所有操作共享数据的线程都要加锁

5.互斥锁相关函数:

  • 初始化互斥锁:
pthread_mutex_init(
					pthread_mutex_t *restrict mutex;
					pthread_mutex_t *restrict attr
);

  • 销毁互斥锁
pthread_mutex_dedtroy(pthread_mutex_t *mutex);

  • 加锁
pthread_mutex_lock(pthread_mutex_t *mutex);
  - mutex:没有被上锁,当前线程会将这把锁锁上
  - 被锁上了,当前线程阻塞
  - 锁被打开的时候解除阻塞

  • 尝试加锁,失败返回,不阻塞
pthread_mutex_trylock(pthread_mutex_t *mutex);
   - 没有锁上,当前线程会给这把锁加锁
   - 如果锁上了:不会阻塞,返回

if(pthread_mutex_trylock(&mutex) == 0){
			//访问共享资源
}
else{
			//错误处理
			//或再次尝试加锁
}

  • 解锁
pthread_mutex_unlock(pthread_mutex_t *mutex);

2.2

造成死锁的原因
1.自己锁自己

pthread_mutex_lock(&mutex);  
pthread_mutex_lock(&mutex);
pthread_mutex_unlock(&mutex);

操作完成之后,一定要解锁

2.假设现有线程1,线程2 | 共享资源A,共享资源B | 锁A,锁B。

  • 线程1对共享资源A加锁成功,mutex_A加锁
  • 线程2对共享资源B加锁成功,mutex_B加锁

  • 线程1访问共享资源B时—>线程1阻塞
  • 线程2访问共享资源A时—>线程2阻塞

解决:
1.让线程按照一定的顺序区访问共享资源
2.在访问其他锁的时候,需要现将自己的锁解开
3.使用trylock

3. 读写锁

1.读写锁是一把锁

  • pthread_rwlock_t lock;

2.读写锁的类型:

  • 读锁 - 对内存做读操作
  • 写锁 - 对内存做写操作

3.读写锁的特性:

  • 线程A加读锁成功,又来了三个线程,做读操作,可以加锁成功
    • 读共享 - 并行处理
  • 线程A加写锁成功,又来了三个线程,做写操作,三个线程阻塞
    • 写独占
  • 线程A加读锁成功,又来了B线程加写锁阻塞,又来了C线程加读锁阻塞
    • 读写不能同时
    • 写的优先级高

5.读写锁的适用场景:

  • 互斥锁 - 读写串行
  • 读写锁:
    • 读:并行
    • 写:串行
  • 程序中的读操作 > 写操作的时候

6.主要操作函数:

  • 初始化读写锁:pthread_rwlock_init();
  • 销毁读写锁:pthread_relock_destory();
  • 加读锁:pthread_rwlock_rdlock();
  • 尝试加读锁:pthread_rwlock_tryrdlock();
  • 加写锁:pthread_rwlcok_wrlock();
  • 尝试加写锁:pthread_rwlcok_trywrlock();
  • 解锁:pthread_rwlock_unlock();

7.读写锁,互斥锁

  • 阻塞线程
  • 不是什么时候都能阻塞线程

4. 条件变量

1.条件变量是锁吗?

  • 不是锁,但是条件变量能后阻塞线程
  • 使用条件变量 + 互斥锁
    • 互斥量:保护一块共享数据
    • 条件变量:引起阻塞
      • 生产者和消费者模型

2.条件变量的两个动作?

  • 条件不满足,阻塞线程
  • 当条件满足,通知阻塞的线程开始工作

3.条件变量的类型:pthread_cond_t;
4.主要函数

  • 初始化一个条件变量 - cond
phread_cond_init(
                pthread_cond_t *restrict cond,
                const pthread_condattr_t *restrict attr
      );

  • 销毁一个条件变量
 pthread_cond_destroy(pthread_cond_t *cond);

  • 阻塞等待一个条件变量
pthread_cond_wait(
      			pthread_cond_t *restrict cond,
      			pthread_mutex_t *restrict mutex
      );
阻塞线程
将mutex解锁
该函数解除阻塞,会对互斥锁加锁

  • 限时等待一个条件变量(阻塞一定的时间)
phtread_cond_timedwait(
   	  			pthread_cond_t *restrict cond,
   	  			pthread_mutex_t *restict mutex,
   	  			const struct timespec *restrict abstime
   	  );

  • 唤醒至少一个阻塞在条件变量上的线程
pthread_cond_signal(pthread_cond_t *cond);
  • 唤醒全部阻塞在条件变量上的线程
pthread_cond_broadcast(pthread_cond_t *cond);

5. 信号量(信号灯)

1.头文件 - semaphore.h
2.信号量类型

  • sem_t sem;
  • 加强版的互斥锁

3.主要函数:

  • 初始化信号量
sem_intit(sem_t *sem, int pshared, unsigned int value);

0 - 线程同步
1 - 进程同步
value - 最多有几个线程操作共享数据

  • 销毁信号量
sem_destroy(sem_t *sem);

  • 加锁 - -
sem_wait(sem_t *sem);
 - 调用一次相当于对sem做了 -- 操作
 - 如果sem值为0,加锁失败,不阻塞,直接返回

  • 限时尝试加锁
sem_trywait(sem_t, *sem);

  • 解锁 ++
sem_post(sem_t *sem);  //对sem做了++操作
  • 1
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值