引言
线程可以理解为一个可以独立拥有时间片的函数。线程无法脱离进程而存在。
同一个进程中的线程共享该进程的地址空间。
一个进程的所有信息对该进程的所有线程都是共享的,包括可执行程序的代码、程序的全局内存和堆内存、栈以及文件描述符。
线程标识
每个线程都有一个线程ID,用于标识线程,用pthread_t结构表示。
比较两个线程ID:
#include <pthread.h>
int pthread_equal(pthread_t tid1, pthread_t tid2);
获取自身的线程ID:
#include <pthread.h>
pthread_t pthread_self(void);
创建线程
声明:
#include <pthread.h>
int pthread_create(pthread_t *restrict tidp, const pthread_attr_t *restrict attr,
void *(*start_rtn)(void *), void *restrict arg);
参数:
tidp: 线程ID
attr:线程属性
start_rtn:线程函数的地址,新创建的线程从start_rtn函数的地址开始运行
arg: 函数start_rtn的参数
线程终止
单个线程可以通过3种方式退出:
(1)线程可以简单地从启动例程中返回,返回值是线程的退出码
(2)线程可以被同一进程中的其他线程取消
#include <pthread.h>
int pthread_cancel(pthread_t tid);
(3)线程调用pthread_exit
#include <pthread.h>
void pthread_exit(void *rval_ptr); //rval_ptr:指定线程的退出信息
进程和线程的区别
- 从概念上区分。进程是系统分配资源的基本单位,而线程是系统调度的基本单位。
进程在运行时,操作系统会为其分配资源,而线程不会被单独分配系统管理的资源。
线程必须依赖于进程提供的运行环境来运行。 - 从时间调度上区分。系统在多进程之间调度的时间开销比多线程调度要大。
因为,各进程的空间是独立的,系统启动一个进程,必须为其分配独立的地址空间,并且建立众多的数据表来维护它的代码段、堆栈段和数据。
而多线程间,它们共享所属进程的大部分资源。
因此,多进程间切换比多线程切换所需时间开销要大得多。 - 从通信方式上区分。线程间通信比进程间通信更方便
在同一进程下的多线程间通信,一般采用全局变量等方式,通过全局变量能够很方便的进行通讯,只需要考虑共享数据之间的同步和互斥即可。
而多进程需要采用较复杂的IPC通信接口进行通信。
多线程同步和互斥
多线程间处理同一份数据时,要考虑数据的同步和互斥。
互斥锁
互斥,即确保同一时间只有一个线程能够访问数据。
互斥量从本质上说是一把锁,在访问共享资源前获取互斥量(即加锁),在访问完成后释放(解锁)互斥量。
对互斥量进行加锁以后,任何其他试图再次对互斥量加锁的线程都会被阻塞直到当前线程释放该互斥锁。
如果释放互斥量时有一个以上的线程阻塞,那么所有该锁上的阻塞线程都会变成可运行状态,而第一个变为可运行状态的线程将获取该锁。
相关函数:
互斥量的创建和销毁:
声明:
#include <pthread.h>
int pthread_mutex_init(pthread_mutex_t *restrict mutex,
const pthread_mutexattr_t *restrict attr);
int pthread_mutex_destroy(pthread_mutex_t *mutex);
参数:
attr:互斥量的属性,为NULL时为默认属性
返回值:
success: 0
fail: 错误编号
互斥量的加锁和解锁函数:
声明:
#include <pthread.h>
int pthread_mutex_lock(pthread_mutex_t *mutex);
int pthread_mutex_trylock(pthread_mutex_t *mutex);
pthread_mutex_trylock将尝试对互斥量进行加锁,如果此时互斥量处于未锁住状态,则将锁住互斥量,否则不能锁住互斥量,并返回EBUSY
int pthread_mutex_unlock(pthread_mutex_t *mutex);
返回值:
success: 0
fail: 错误编号
读写锁
读写锁和互斥锁的区别在于,互斥锁只有两种状态,一个是加锁,另一个是解锁,而读写锁有三种状态,一个是读加锁,一个是写加锁,另一个是解锁。
读写锁规定,读锁能被多个线程加锁,而写锁只能被一个线程加锁。即读共享,写独占。
读写锁适用于对数据结构读的次数远大于写的情况。
相关函数:
读写锁的创建和销毁函数:
声明:
#include <pthread.h>
int pthread_rwlock_init(pthread_rwlock_t *restrict rwlock,
const pthread_rwlockattr_t *restrict attr);
int pthread_rwlock_destroy(pthread_rwlock_t *rwlock);
参数:
attr:互斥量的属性,为NULL时为默认属性
返回值:
success: 0
fail: 错误编号
读写锁的加锁和解锁函数:
声明:
#include <pthread.h>
int pthread_rwlock_rdlock(pthread_rwlock_t *rwlock);
int pthread_rwlock_wrlock(pthread_rwlock_t *rwlock);
int pthread_rwlock_unlock(pthread_rwlock_t *rwlock);
返回值:
success: 0
fail: 错误编号
条件变量
条件变量用于某个线程需要在某种条件下成立时,才去保护它将要操作的临界区这种情况下。
一般采用互斥锁+条件变量:
条件变量提供了一个功能,就是在等待条件成立过程中,先进行了解锁,避免该锁在其他线程无法加锁, 这是单独用互斥锁无法实现的功能。
条件变量的创建和销毁函数:
声明:
#include <pthread.h>
int pthread_cond_init(pthread_cond_t *restrict cond,
const pthread_condattr_t *restrict attr);
int pthread_cond_destroy(pthread_cond_t *cond);
等待条件变量函数:
声明:
#include <pthread.h>
int pthread_cond_wait(pthread_cond_t *restrict cond,
pthread_mutex_t *restrict mutex);
int pthread_cond_timewait(pthread_cond_t *restrict cond,
pthread_mutex_t *restrict mutex,
const struct timespec *restrict tsptr);
参数:
cond: 条件变量
mutex:互斥量
通知线程条件已经满足:
声明:
#include <pthread.h>
int pthread_cond_signal(pthread_cond_t *cond);
int pthread_cond_broadcast(pthread_cond_t *cond);
给条件发信号
参数:
cond: 条件变量
信号量
多用于线程间,也可以用于多进程
API:
sem_init
sem_post: 信号量值加1
sem_wait: 信号量值减1
sem_destory:销毁信号量