LinuxC/C++ 线程、锁和条件变量

14 篇文章 2 订阅

LinuxC/C++ 线程、锁和条件变量



线程

线程是操作系统能够进行运算调度的最小单位。它被包含在进程之中,是进程中的实际运作单位. 一条线程指的是进程中一个单一顺序的控制流,一个进程中可以并发多个线程,每条线程并行执行不同的任务,它们共享该进程所拥有的资源.

Linux下线程默认栈的大小为8M,可以使用ulimit -s命令查看:

在这里插入图片描述


创建线程

首先需要手动链接pthread.h头文件.
如果使用CMake,需要在CMakeLists.txt文件最后加上:

# first是编译后二进制文件名
TARGET_LINK_LIBRARIES(first pthread)

pthread.h直接提供了创建线程的API:

#include <pthread.h>

// 成功返回0,失败返回错误编号
int pthread_create(pthread_t *restrict tidp, 
					const pthread_attr_t *restrict attr,
					void *(start_rtn)(void *),
					void *restrict arg);
  • tidp:线程Id.
  • attr:用于定制线程的属性,默认则置为NULL.
  • start_rtn:线程创建成功后运行的回调函数.
  • arg:传进start_rtn函数的参数.

线程要执行的回调函数:

void *thread_callback(void *arg) {
	// TODO
}

终止线程

#include <pthread.h>

void pthread_exit(void *rval_ptr);
  • retval表示线程退出状态,通常传NULL.

用于强制退出线程(还未执行完毕).

阻塞线程

#include <pthread.h>

// 
int pthread_join(pthread thread, void **rval_ptr);
  • pthread_t thread: 待阻塞线程的线程号.
  • void **retval : 指向一个指向阻塞线程的返回码的指针的指针,一般传NULL.

pthread_join()主要有两种作用:

  1. 用于等待其他线程结束:当调用 pthread_join() 时,当前线程会处于阻塞状态,直到被调用的线程结束后,当前线程才会重新开始执行.
  2. 对线程的资源进行回收:如果一个线程是非分离的(默认情况下创建的线程都是非分离)并且没有对该线程使用 pthread_join() 的话,该线程结束后并不会释放其内存空间,这会导致该线程变成了“僵尸线程”.

互斥锁

并发环境下为了保证线程安全,可以对临界资源加上锁,确保同一时间只有一个线程访问数据. 互斥锁加锁失败后,线程释放CPU,给其他线程.

初始化锁:

#include <pthread>

int pthread_mutex_init(pthread_mutex_t *restrict mutex,
						const pthread_mutexattr_t *restrict attr);
  • mutex:待初始化的锁.
  • attr:锁的属性,默认则设置为NULL.

加锁和解锁:

#include <pthread>

int pthread_mutex_lock(pthread_mutex_t * mutex);
int pthread_mutex_unlock(pthread_mutex_t * mutex);

自旋锁

自旋锁加锁失败后,线程会忙等待,直到它拿到锁.

初始化锁:

#include <pthread>

int pthread_spin_init(pthread_spinlock_t *lock,
						int pshared);

加锁和解锁:

#include <pthread>

int pthread_spin_lock(pthread_spinlock_t *lock);
int pthread_spin_unlock(pthread_spinlock_t *lock);

使用互斥锁还是自旋锁主要看临界区的工作量怎么样,如果工作量小(付出的代价小于线程切换上下文)可以使用自旋锁,如果工作量大(付出的代价大于线程切换上下文)则可以选择使用互斥锁.

条件变量

上面提到的pthread_join()函数可以阻塞当前的线程,等待子线程执行完毕,实际上是一种线程同步的机制,而条件变量则是线程同步的另外一种机制.
条件变量一般与互斥锁一起使用,互斥锁只有两种状态:锁定和非锁定,而条件变量可以通过线程阻塞和等待另一个线程发送信号, 从而弥补互斥锁的不足.

初始化条件变量

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

让当前线程阻塞等待

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);

条件变量的使用

A线程:

pthread_mutex_lock(&mutex);
while (false == ready) {
     pthread_cond_wait(&cond, &mutex);
}
pthread_mutex_unlock(&mutex); 

B线程:

pthread_mutex_lock(&mutex);
ready = true;
pthread_mutex_unlock(&mutex);
pthread_cond_signal(&cond);

一共会出现两种情况:

  1. 如果 Thread A 先拿到 mutex,那么此时 ready 为 false,Thread A 调用 pthread_cond_wait 进入等待队列,接着释放 mutex,然后 Thread B 才能修改 ready,并 signal.
  2. 如果 Thread A 没有拿到 mutex,Thread B 拿到 mutex,然后修改 ready 为 true,然后释放锁,这样 Thread A 在拿到 mutex,就不会再进 while 循环调 wait 了.

  • 1
    点赞
  • 2
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值