[Linux]多线程(二)原生线程库---pthread库的使用

[Linux]多线程(二)原生线程库---pthread库的使用
@水墨不写bug



文章目录


一、pthread原生线程库的使用

1. pthread_create

作用 :创建新线程。
函数原型

c 复制代码
int pthread_create(pthread_t *thread, const pthread_attr_t *attr,
                   void *(*start_routine)(void *), void *arg);

参数

  • thread:输出型参数,存储新线程的标识符(pthread_t类型)。
  • attr:线程属性(如栈大小、分离状态),若为NULL则使用默认属性。
  • start_routine:线程入口函数,必须为void* (*)(void*)类型。
  • arg:传递给入口函数的参数(void*类型);

全面的看待线程返回值

传递进来的参数并不一定是一个,也可以是一个ThreadData类,提前在这个类中设置好要使用的参数,那么我们就可以给新线程传递多个参数,甚至方法了!!把这个类当做一个用于线程传参的辅助类。

以及新线程执行的函数的返回值也可以是一个类:ThreadResult,把这个类看做是用于传递返回值的辅助类。 在主线程创建一个ThreadResult* 的对象tr,(void* *)&tr 即可获取返回值。

此外,主线程的传递给新线程的参数最好是堆区创建的变量:

如果是栈区的变量,创建第二个线程如果还用这个变量,也会对第一个新线程产生影响

如果是堆区的变量,那么传递给新线程之后,就相当于把这个堆区指针交给新线程维护了

返回值

  • 成功返回0,失败返回错误码(非errno,需用strerror转换)。

注意事项

  • 线程创建后立即执行,需同步共享数据。
  • 确保传递的arg指针在子线程中有效(避免悬垂指针)。
  • 默认创建的线程是"可连接的"(需要pthread_join回收资源)。

如何创建多线程?

通过循环,但是要注意,需要每次都从堆区新申请空间,而不能使用栈区空间:
例子一:

例子二:


2. pthread_join

一旦我们通过pthread_create 创建了一个线程,这个线程与主线程谁先执行是不确定的(因为线程复用的进程的调度算法,父子进程谁先执行是不确定的 ),但是一般我们希望主线程最后退出,因为主线程需要回收新线程的资源;如果给新线程安排的任务没有完成主线程就退出了,那么新线程也一并退出(这种情况不可能出现,因为新线程的任务必须要完成)。新线程需要主线程调用pthread_join回收。
作用 :等待线程终止,并回收其资源。
函数原型

c 复制代码
int pthread_join(pthread_t thread, void **retval); 

参数

  • thread:目标线程的标识符。
  • retval:输出参数,接收线程的返回值(若为NULL则忽略返回值)。
    retval是二级指针的原因是新线程的返回值是void* 类型的指针,如果想要通过传参获取这个返回值,就需要void* 的地址,也就是void**。

返回值

  • 成功返回0,失败返回错误码。

注意事项

  • 只能对非分离(joinable)线程调用,否则返回EINVAL
  • 调用这个函数调用线程会阻塞,直到目标线程终止。
  • 必须调用pthread_joinpthread_detach避免资源泄漏,否则会出现类似于僵尸进程的线程数据结构未被释放。

3. pthread_exit

作用 :终止当前线程,并传递返回值。
函数原型

c 复制代码
void pthread_exit(void *retval); 

参数

  • retval:线程的返回值(可为NULL)。

注意事项

  • 主线程调用pthread_exit不会终止进程,其他线程继续运行。
  • 若线程未分离,返回值需由其他线程通过pthread_join获取。
  • 不要在线程中返回指向局部变量的指针(栈内存会被销毁回收)。

对比理解线程退出?

在了解线程之前,我们知道的进程退出的方法有:1、return 2、exit()。

但是在有了线程的概念之后,我们需要读线程进行更加细致的区分。以及对于线程退出,pthread库提供了多个方法。

1、return退出

主线程return就是进程结束;新线程return代表这个线程退出,返回的就是void* 类型的指针。

2、调用C库函数exit()退出

主线程和新线程都一样,只要调用eixt函数,代表整个进程退出。

3、调用pthread库函数pthread_exit()退出

对于新线程而言,调用pthread_exit就相当于return,并且pthread_eixt参数也和return的返回值相同,也是void* 指针。

对于主线程而言,调用pthread_exit函数,如果新线程没有退出,则阻塞等待新线程退出;如果新线程已经退出,则直接退出。

4、被pthread_cancel取消

c 复制代码
int pthread_cancel(pthread_t thread);

一般通过主线程取消其他线程(用其他线程取消主线程,主线程退出,其他线程一并退出,这个结果是未定义的)。如果一个线程被取消了,那么这个线程的返回值是-1,这代表什么意思?

在pthread库中,定义了PTHREAD_CANCELED宏:

c 复制代码
#define PTHREAD_CANCELED ((void*)-1)

这个宏标识了被取消的线程是通过pthread_cancel取消结束的。

运行结果:


理解线程分离?

如果一个线程不想被主线程回收,可以调用pthread_detach(pthead_self())或者主线程调用pthread_detach(新线程tid)。调用之后,这个线程还是属于进程内部,但是主线程可以专注于做自己的事情,而不再需要回收新线程。新线程调用之后,执行完之后直接退出并回收资源。

而一个分离的线程如果出错,OS仍然是向进程发送信号,让整个进程退出。

4. pthread_detach

作用 :将线程标记为分离状态,线程终止后自动释放资源。
函数原型

c 复制代码
int pthread_detach(pthread_t thread); 

参数

  • thread:目标线程的标识符。

返回值

  • 成功返回0,失败返回错误码。

注意事项

  • 分离线程无法再被pthread_join
  • 可在线程内部调用pthread_detach(pthread_self())自我分离。

5. 互斥锁(Mutex)函数

作用:保护共享资源,防止竞态条件。

pthread_mutex_init

c 复制代码
int pthread_mutex_init(pthread_mutex_t *mutex, const pthread_mutexattr_t *attr); 
  • 初始化互斥锁,attr为属性(NULL表示默认)。

pthread_mutex_lock / pthread_mutex_unlock

c 复制代码
int pthread_mutex_unlock(pthread_mutex_t *mutex); 
  • 加锁(阻塞)和解锁。

pthread_mutex_destroy

c 复制代码
int pthread_mutex_destroy(pthread_mutex_t *mutex); 
  • 销毁互斥锁,释放资源。

注意事项

  • 静态初始化可用PTHREAD_MUTEX_INITIALIZER
  • 确保每次加锁后解锁,避免死锁。
  • 锁的持有时间应尽量短,减少性能影响。

6. 条件变量(Condition Variable)函数

作用:线程间通知机制,需与互斥锁配合使用。

pthread_cond_init

c 复制代码
int pthread_cond_init(pthread_cond_t *cond, const pthread_condattr_t *attr); 
  • 初始化条件变量。

pthread_cond_wait

c 复制代码
int pthread_cond_wait(pthread_cond_t *cond, pthread_mutex_t *mutex); 
  • 释放mutex并等待条件变量,被唤醒后重新获得锁。

pthread_cond_signal / pthread_cond_broadcast

c 复制代码
int pthread_cond_signal(pthread_cond_t *cond);  // 唤醒至少一个线程 int
pthread_cond_broadcast(pthread_cond_t *cond); // 唤醒所有线程 

注意事项

  • 使用循环检查条件,防止虚假唤醒。
  • 调用pthread_cond_wait前必须持有mutex

7. 线程属性函数

作用:设置线程属性(如分离状态、栈大小)。

pthread_attr_init / pthread_attr_destroy

c 复制代码
int pthread_attr_destroy(pthread_attr_t *attr); 
  • 初始化和销毁属性对象。

设置分离状态

c 复制代码
pthread_attr_setdetachstate(&attr, PTHREAD_CREATE_DETACHED); 
  • 创建线程时直接设置为分离状态。

其他函数

  • pthread_self():获取当前线程ID。
  • pthread_equal(t1, t2):比较线程ID是否相等。

C++11的线程库是对pthread原生线程库的封装

在不同的语言中,可能会有不同的线程库,在Linux下,不同线程库都是对原生线程库的封装。而C++也不例外,在C++11引入的thread库是对pthread库的封装。


完~
转载请注明出处

相关推荐
liuyunluoxiao7 分钟前
进程间通信--管道【Linux操作系统】
linux
来一杯龙舌兰7 分钟前
【Bug经验分享】SourceTree用户设置必须被修复/SSH 主机密钥未缓存(踩坑)
运维·缓存·ssh·sourcetree·主机密钥未缓存
未来之窗软件服务8 分钟前
智慧农业、智慧养殖平台—监控摄像头管理监控设计—仙盟创梦IDE
运维·ide·智慧农业·智慧养殖·仙盟创梦ide
翻滚吧键盘10 分钟前
debian中笔记本的省电选择auto-cpufreq
运维·debian
辰尘_星启29 分钟前
【Debian】关于LubanCat-RK3588s开发板安装Debian的一些事
运维·ubuntu·debian
NON-JUDGMENTAL34 分钟前
CentOS 7 修改锁屏时间为永不
linux·运维·centos
袁震40 分钟前
android HashMap和List该如何选择
android·hashmap·sparsearray
努力努力再努力wz40 分钟前
【Linux实践系列】:进程间通信:万字详解共享内存实现通信
java·linux·c语言·开发语言·c++
jbjhzstsl1 小时前
鱼眼摄像头(一)多平面格式 单缓冲读取图像并显示
linux·图像处理
Li.Yc1 小时前
Linux 学习笔记2
linux·笔记·学习