线程是操作系统能够进行运算调度的最小单位。它被包含在进程之中,是进程中的实际运作单位. 一条线程指的是进程中一个单一顺序的控制流,一个进程中可以并发多个线程,每条线程并行执行不同的任务,它们共享该进程所拥有的资源.
Linux下线程默认栈的大小为8M,可以使用ulimit -s
命令查看:
创建线程
首先需要手动链接pthread.h
头文件.
如果使用CMake,需要在CMakeLists.txt
文件最后加上:
# first是编译后二进制文件名
TARGET_LINK_LIBRARIES(first pthread)
pthread.h
直接提供了创建线程的API:
#include
// 成功返回0,失败返回错误编号
int pthread_create(pthread_t *restrict tidp,
const pthread_attr_t *restrict attr,
void *(start_rtn)(void *),
void *restrict arg);
线程要执行的回调函数:
void *thread_callback(void *arg) {
// TODO
}
终止线程
#include
void pthread_exit(void *rval_ptr);
用于强制退出线程(还未执行完毕).
阻塞线程
#include <pthread.h>
//
int pthread_join(pthread thread, void **rval_ptr);
pthread_join()
主要有两种作用:
pthread_join()
的话,该线程结束后并不会释放其内存空间,这会导致该线程变成了“僵尸线程”.互斥锁
并发环境下为了保证线程安全,可以对临界资源加上锁,确保同一时间只有一个线程访问数据. 互斥锁加锁失败后,线程释放CPU,给其他线程.
初始化锁:
#include
int pthread_mutex_init(pthread_mutex_t *restrict mutex,
const pthread_mutexattr_t *restrict attr);
加锁和解锁:
#include
int pthread_mutex_lock(pthread_mutex_t * mutex);
int pthread_mutex_unlock(pthread_mutex_t * mutex);
自旋锁
自旋锁加锁失败后,线程会忙等待,直到它拿到锁.
初始化锁:
#include
int pthread_spin_init(pthread_spinlock_t *lock,
int pshared);
加锁和解锁:
#include
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);
一共会出现两种情况:
pthread_cond_wait
进入等待队列,接着释放 mutex,然后 Thread B 才能修改 ready,并 signal.