Linux多线程开发
2024-06-18 14:47:05 4 举报
AI智能生成
Linux后端
作者其他创作
大纲/内容
线程
简介
线程(thread)是允许应用程序并发执行多个任务的一种机制
一个进程可以包含多个线程。同一个程序中的所有线程均会独立执行相同程序,且共
享同一份全局内存区域,其中包括初始化数据段、未初始化数据段,以及堆内存段
享同一份全局内存区域,其中包括初始化数据段、未初始化数据段,以及堆内存段
传统意义上的 UNIX 进程只是多线程程序的一个特例,该进程只包含一个线程
线程是轻量级的进程(LWP:Light Weight Process),在 Linux 环境下线程的本质仍是进程
查看指定进程的 LWP 号:ps –Lf pid
线程和进程区别
进程是 CPU 分配资源的最小单位,线程是操作系统调度执行的最小单位。
进程间的信息难以共享。由于除去只读代码段外,父子进程并未共享内存,因此必须采用
一些进程间通信方式,在进程间进行信息交换
一些进程间通信方式,在进程间进行信息交换
调用 fork() 来创建进程的代价相对较高,即便利用写时复制技术,仍然需要复制诸如
内存页表和文件描述符表之类的多种进程属性,这意味着 fork() 调用在时间上的开销
依然不菲
内存页表和文件描述符表之类的多种进程属性,这意味着 fork() 调用在时间上的开销
依然不菲
线程之间能够方便、快速地共享信息。只需将数据复制到共享(全局或堆)变量中即可
创建线程比创建进程通常要快 10 倍甚至更多。线程间是共享虚拟地址空间的,无需采
用写时复制来复制内存,也无需复制页表
用写时复制来复制内存,也无需复制页表
线程和进程的虚拟地址空间
进程的栈空间、text段被分为一小块一小块交给各个线程
共享库、堆空间等各个线程共享
线程之间共享和非共享资源
共享资源
进程 ID 和父进程 ID
进程组 ID 和会话 ID
用户 ID 和 用户组 ID
文件描述符表
信号处置
文件系统的相关信息
文件权限掩码
当前工作目录
虚拟地址空间(除了栈和text段)
非共享资源
线程 ID
信号掩码
线程特有数据
error 变量
实时调度策略和优先级
栈,本地变量和函数的调用链接信息
线程操作
函数
创建管理线程
pthread_create()
函数原型
int pthread_create(pthread_t *thread, const pthread_attr_t *attr,
void *(*start_routine) (void *), void *arg);
void *(*start_routine) (void *), void *arg);
功能
创建一个线程
参数
thread
一个指向pthread_t类型的指针,用于存储新创建线程的 ID
attr
一个指向pthread_attr_t类型的指针,用于设置线程属性
如果设置为NULL,则使用默认属性
start_routine
线程开始执行的函数指针
arg
传递给start_routine的参数
返回值
成功时返回0
失败时返回错误号
pthread_self()
函数原型
pthread_t pthread_self(void)
功能
返回调用线程的线程ID
返回值
调用线程的pthread_t类型的线程 ID
pthread_equal(t1, t2)
函数原型
int pthread_equal(pthread_t t1, pthread_t t2
参数
t1和t2,要比较的两个线程ID
返回值
相等返回非0值
否则返回0
pthread_exit()
函数原型
void pthread_exit(void *retval)
参数
retval,一个指针,指向线程的返回值
注意
该函数不会返回
pthread_join()
函数原型
int pthread_join(pthread_t thread, void **retval);
参数
thread:要等待的线程的线程 ID
retval:一个指向指针的指针,用于存储线程的返回值
返回值
成功时返回0
失败时返回错误号
pthread_detach()
函数原型
int pthread_detach(pthread_t thread);
功能
分离线程,使其在终止时自动释放所有资源
参数
thread:要分离的线程的线程 ID
返回值
成功时返回0
失败时返回错误号
pthread_cancel()
函数原型
int pthread_cancel(pthread_t thread);
功能
发送取消请求给指定的线程
参数
thread:要取消的线程的线程 ID
返回值
成功时返回0
失败时返回错误号
设置线程属性
初始化和销毁
pthread_attr_init(pthread_attr_t *attr)
初始化线程属性对象,为其设置默认值
pthread_attr_destroy(pthread_attr_t *attr)
销毁线程属性对象,释放任何与之相关的资源
堆栈大小
pthread_attr_setstacksize(pthread_attr_t *attr, size_t stacksize)
设置线程堆栈大小
pthread_attr_getstacksize(const pthread_attr_t *attr, size_t *stacksize)
获取线程堆栈大小
调度策略和优先级
pthread_attr_setschedpolicy(pthread_attr_t *attr, int policy)
设置线程的调度策略(如 SCHED_FIFO, SCHED_RR 或 SCHED_OTHER)
pthread_attr_getschedpolicy(const pthread_attr_t *attr, int *policy)
获取线程的调度策略
pthread_attr_setschedparam(pthread_attr_t *attr, const struct sched_param *param)
设置线程的调度参数,通常用于设置线程的优先级
pthread_attr_getschedparam(const pthread_attr_t *attr, struct sched_param *param)
获取线程的调度参数
分离状态
pthread_attr_setdetachstate(pthread_attr_t *attr, int detachstate)
设置线程的分离状态
例如
PTHREAD_CREATE_DETACHED(创建后立即分离)
PTHREAD_CREATE_JOINABLE(可以被其他线程 join)
pthread_attr_getdetachstate(const pthread_attr_t *attr, int *detachstate)
获取线程的分离状态
继承调度属性
pthread_attr_setinheritsched(pthread_attr_t *attr, int inherit)
设置线程是否应从创建它的线程继承调度属性
pthread_attr_getinheritsched(const pthread_attr_t *attr, int *inherit)
获取线程的继承调度属性设置
类型
pthread_t
用于表示线程标识符的数据类型
特性
不透明性
程序员通常不需要(也不应该)知道其内部结构或如何工作
唯一性
每个线程都有一个与之关联的唯一的 pthread_t 值
比较
不能用==来比较两个线程,要用pthread_equal()函数进行比较
初始化
当使用 pthread_create() 函数创建一个新线程时,新线程的 pthread_t 值会被存储在传递给该函数的 pthread_t 变量中
用途
主要用于线程管理和操作
例如
等待线程完成(pthread_join())
取消线程(pthread_cancel())
查询线程属性
pthread_attr_t
不透明,但是提供了许多操作
线程同步
线程的优势
线程的主要优势在于,能够通过全局变量来共享信息
必须确保多个线程不会同时修改同一变量,或者某一线程不会读取正在由其他线程
修改的变量
修改的变量
临界区
问某一共享资源的代码片段,并且这段代码的执行应为原子操作
同时访问同一共享资源的其他线程不应终端该片段的执行
线程同步
即当有一个线程在对内存进行操作时,其他线程都不可以对这个内存地址进
行操作
行操作
该线程完成操作,其他线程才能对该内存地址进行操作,而其他线程则处
于等待状态
于等待状态
互斥量
简介
为避免线程更新共享变量时出现问题,可以使用互斥量(mutex 是 mutual exclusion
的缩写)来确保同时仅有一个线程可以访问某项共享资源.
的缩写)来确保同时仅有一个线程可以访问某项共享资源.
状态
互斥量有两种状态:已锁定(locked)和未锁定(unlocked)
任何时候,至多只有一个线程可以锁定该互斥量
试图对已经锁定的某一互斥量再次加锁,将可能阻塞线程或者报
错失败,具体取决于加锁时使用的方法
错失败,具体取决于加锁时使用的方法
所有者
一旦线程锁定互斥量,随即成为该互斥量的所有者,只有所有者才能给互斥量解锁
一般情况下,对每一共享资源(可能由多个相关变量组成)会使用不同的互斥量
每一线程在访问同一资源时将采用如下协议
针对共享资源锁定互斥量
访问共享资源
对互斥量解锁
阻塞
如果多个线程试图执行这一块代码(一个临界区),事实上只有一个线程能够持有该互斥
量(其他线程将遭到阻塞),即同时只有一个线程能够进入这段代码区域
量(其他线程将遭到阻塞),即同时只有一个线程能够进入这段代码区域
相关函数
定义和初始化
静态初始化
pthread_mutex_t mutex = PTHREAD_MUTEX_INITIALIZER;
函数动态初始化
pthread_mutex_init()
函数原型
int pthread_mutex_init(pthread_mutex_t *restrict mutex,
const pthread_mutexattr_t *restrict attr);
const pthread_mutexattr_t *restrict attr);
功能
初始化一个互斥量
参数
mutex:指向要初始化的互斥量的指针
attr:指向pthread_mutexattr_t的指针,用于设置互斥量的属性
如果使用默认属性,可以设置为NULL
返回值
成功时返回0
失败时返回错误号
锁定和解锁
pthread_mutex_lock()
函数原型
int pthread_mutex_lock(pthread_mutex_t *mutex)
功能
锁定互斥量
参数
mutex:要解锁的互斥量的指针
返回值
成功时返回0
失败时返回错误号
pthread_mutex_trylock()
函数原型
int pthread_mutex_trylock(pthread_mutex_t *mutex);
功能
尝试锁定互斥量,但不阻塞
参数
mutex:要尝试锁定的互斥量的指针
返回值
成功时返回0
如果互斥量已被锁定则返回EBUSY
其他失败情况返回错误号
pthread_mutex_unlock():
函数原型
int pthread_mutex_unlock(pthread_mutex_t *mutex);
功能
解锁互斥量
参数
mutex:要解锁的互斥量的指针
返回值
成功时返回0
失败时返回错误号
销毁
pthread_mutex_destroy()
函数原型
int pthread_mutex_destroy(pthread_mutex_t *mutex);
功能
销毁一个已初始化的互斥量
参数
mutex:要销毁的互斥量的指针
返回值
成功时返回0
失败时返回错误号
注意
在销毁互斥量之前,必须确保它是未锁定的,并且没有其他线程正在等待它。
互斥量属性 (pthread_mutexattr_t)
pthread_mutexattr_init()
函数原型
int pthread_mutexattr_init(pthread_mutexattr_t *attr);
参数
attr:指向要初始化的互斥量属性对象的指针
返回值:成功时返回0,失败时返回错误号
pthread_mutexattr_destroy():
函数原型
int pthread_mutexattr_destroy(pthread_mutexattr_t *attr);
参数
attr:指向要销毁的互斥量属性对象的指针
返回值
成功时返回0
失败时返回错误号
类型
pthread_mutex_t,不透明
死锁
场景
有时,一个线程需要同时访问两个或更多不同的共享资源,而每个资源又都由不同的互
斥量管理
斥量管理
当超过一个线程加锁同一组互斥量时,就有可能发生死锁
定义
两个或两个以上的进程在执行过程中,因争夺共享资源而造成的一种互相等待的现象,
若无外力作用,它们都将无法推进下去。此时称系统处于死锁状态或系统产生了死锁
若无外力作用,它们都将无法推进下去。此时称系统处于死锁状态或系统产生了死锁
常见原因
忘记释放锁
重复加锁
多线程多锁,抢占锁资源
读写锁
场景
当有一个线程已经持有互斥锁时,互斥锁将所有试图进入临界区的线程都阻塞住。
但是考虑一种情形,当前持有互斥锁的线程只是要读访问共享资源,而同时有其它几个线程也想
读取这个共享资源
读取这个共享资源
但是由于互斥锁的排它性,所有其它线程都无法获取锁,也就无法读访问共享资源了,但是实际上多个线程同时读访问共享资源并不会导致问题
定义
在对数据的读写操作中,更多的是读操作,写操作较少,例如对数据库数据的读写应用
为了满足当前能够允许多个读出,但只允许一个写入的需求,线程提供了读写锁来实现
特点
如果有其它线程读数据,则允许其它线程执行读操作,但不允许写操作
如果有其它线程写数据,则其它线程都不允许读、写操作
写是独占的,写的优先级高
相关函数
定义和初始化
静态初始化
pthread_rwlock_t rwlock = PTHREAD_RWLOCK_INITIALIZER;
动态初始化
pthread_rwlock_init()
函数原型
int pthread_rwlock_init(pthread_rwlock_t *rwlock, const pthread_rwlockattr_t *attr);
功能
初始化一个读写锁。
参数
rwlock:指向要初始化的读写锁的指针
attr
指向pthread_rwlockattr_t的指针,用于设置读写锁的属性
如果使用默认属性,可以设置为NULL
返回值
成功时返回0
失败时返回错误号
读锁定和解锁
pthread_rwlock_rdlock()
函数原型
int pthread_rwlock_rdlock(pthread_rwlock_t *rwlock);
功能
为读操作锁定读写锁
参数
rwlock:要锁定的读写锁的指针。
返回值
成功时返回0
失败时返回错误号
pthread_rwlock_tryrdlock()
函数原型
int pthread_rwlock_tryrdlock(pthread_rwlock_t *rwlock);
功能
尝试为读操作锁定读写锁,但不阻塞
参数
rwlock:要尝试锁定的读写锁的指针
返回值
成功时返回0
如果读写锁已被写锁定则返回EBUSY
其他失败情况返回错误号
pthread_rwlock_unlock():
函数原型
int pthread_rwlock_unlock(pthread_rwlock_t *rwlock);
功能
解锁读写锁
参数
rwlock:要解锁的读写锁的指针
返回值
成功时返回0
失败时返回错误号
写锁定和解锁
pthread_rwlock_wrlock()
函数原型
int pthread_rwlock_wrlock(pthread_rwlock_t *rwlock);
功能
为写操作锁定读写锁
参数
rwlock:要锁定的读写锁的指针
返回值
成功时返回0
失败时返回错误号
pthread_rwlock_trywrlock()
函数原型
int pthread_rwlock_trywrlock(pthread_rwlock_t *rwlock);
功能
尝试为写操作锁定读写锁,但不阻塞
参数
rwlock:要尝试锁定的读写锁的指针
返回值
成功时返回0
如果读写锁已被读锁定或写锁定则返回EBUSY
他失败情况返回错误号
销毁
pthread_rwlock_destroy()
函数原型
int pthread_rwlock_destroy(pthread_rwlock_t *rwlock);
功能
销毁一个已初始化的读写锁
参数
rwlock:要销毁的读写锁的指针
返回值
成功时返回0
失败时返回错误号
类型
pthread_rwlock_t
不透明
生产者消费者模型
条件变量
简介
允许线程等待某个条件成为真,而在等待时释放互斥锁
当其他线程改变了这个条件并发出通知时,等待的线程会被唤醒并重新尝试获得互斥锁
类型
pthread_cond_t
相关函数
初始化
pthread_cond_init()
函数原型
int pthread_cond_init(pthread_cond_t *cond, const pthread_condattr_t *attr);
功能
初始化一个条件变量
参数
cond:指向要初始化的条件变量的指针。
attr
指向pthread_condattr_t的指针,用于设置条件变量的属性
如果使用默认属性,可以设置为NULL
返回值
成功时返回0
失败时返回错误号
等待
pthread_cond_wait()
功能
等待条件变量
函数原型
int pthread_cond_wait(pthread_cond_t *cond, pthread_mutex_t *mutex);
参数
cond:要等待的条件变量的指针
mutex
与条件变量关联的互斥锁的指针
调用此函数前,线程必须持有该互斥锁。
介绍
当线程需要等待某个条件成为真时,它会使用 pthread_cond_wait 函数
这个函数会自动释放与条件变量关联的互斥锁,并使线程进入阻塞状态
当条件变量被通知时,线程会被唤醒并重新尝试获得互斥锁
有时限的等待
pthread_cond_timedwait()
函数原型
int pthread_cond_timedwait(pthread_cond_t *cond, pthread_mutex_t *mutex, const struct timespec *abstime);
功能
在指定的时间内等待条件变量
参数
cond:要等待的条件变量的指针。
mutex:与条件变量关联的互斥锁的指针
abstime
指定等待的绝对时间
如果在这个时间之前条件变量没有被通知,函数会返回
返回值
成功时返回0
如果超时则返回ETIMEDOUT
其他失败情况返回错误号
介绍
pthread_cond_timedwait 函数允许线程等待一个指定的时间段
如果在这段时间内条件没有变为真,函数会返回
通知
单个通知
使用 pthread_cond_signal 函数唤醒一个等待条件变量的线程
pthread_cond_signal()
功能
唤醒一个等待条件变量的线程。
函数原型
int pthread_cond_signal(pthread_cond_t *cond);
参数
cond:要通知的条件变量的指针
返回值
成功时返回0
失败时返回错误号
广播通知
使用 pthread_cond_broadcast 函数唤醒所有等待条件变量的线程
pthread_cond_broadcast()
功能
唤醒所有等待条件变量的线程
函数原型
int pthread_cond_broadcast(pthread_cond_t *cond);
参数
cond:要通知的条件变量的指针
返回值
成功时返回0
失败时返回错误号
销毁
pthread_cond_destroy():
功能
销毁一个已初始化的条件变量
函数原型
int pthread_cond_destroy(pthread_cond_t *cond);
参数
cond:要销毁的条件变量的指针
返回值
成功时返回0
失败时返回错误号
注意
在调用 pthread_cond_wait 或 pthread_cond_timedwait 之前,线程必须持有与条件变量关联的互斥锁
当条件变量被通知并唤醒线程时,线程会自动重新获得互斥锁
为了避免虚假唤醒,通常在一个循环中检查条件,并在条件不满足时调用 pthread_cond_wait
信号量
简介
同步原语,用于控制多个线程对共享资源的访问
是一个整数值,通常用于表示可用资源的数量
提供了两个主要的操作:wait(或P操作)和signal(或V操作)
基本概念和操作
初始化
信号量可以被初始化为任何非负整数值,表示资源的初始数量
wait (P操作)
当线程想要获取一个资源时,它会执行wait操作
这会导致信号量的值减少1
如果信号量的值在操作前已经是0,那么执行wait操作的线程会被阻塞,直到其他线程释放资源
signal (V操作)
当线程释放一个资源时,它会执行signal操作
这会导致信号量的值增加1
如果有线程因为wait操作而被阻塞,那么一个线程会被唤醒
相关函数
sem_init()
函数原型
int sem_init(sem_t *sem, int pshared, unsigned int value);
功能
初始化一个信号量
参数
sem指向要初始化的信号量的指针
pshared
如果设置为非零值,信号量将在进程之间共享
否则,它只能在同一进程的线程之间共享
value
信号量的初始值
sem_wait()
函数原型
int sem_wait(sem_t *sem);
功能
减少信号量的值
如果信号量的值为0,则调用线程会被阻塞
参数
sem:要操作的信号量的指针
sem_post()
函数原型
int sem_post(sem_t *sem);
功能
增加信号量的值,并唤醒任何等待的线程
参数
sem:要操作的信号量的指针
sem_destroy()
函数原型
int sem_destroy(sem_t *sem);
功能
销毁一个信号量,释放其相关的资源
参数
sem:要操作的信号量的指针
0 条评论
下一页