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

发布于:2025-05-11 ⋅ 阅读:(30) ⋅ 点赞:(0)

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


在这里插入图片描述



一、pthread原生线程库的使用

1. pthread_create

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

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回收。

作用:等待线程终止,并回收其资源。
函数原型

int pthread_join(pthread_t thread, void **retval); 

参数

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

返回值

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

注意事项

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

3. pthread_exit

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

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取消

int pthread_cancel(pthread_t thread);

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

#define PTHREAD_CANCELED ((void*)-1)

这个宏标识了被取消的线程是通过pthread_cancel取消结束的。
在这里插入图片描述
运行结果:
在这里插入图片描述


理解线程分离?

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

4. pthread_detach

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

int pthread_detach(pthread_t thread); 

参数

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

返回值

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

注意事项

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

5. 互斥锁(Mutex)函数

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

pthread_mutex_init

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

pthread_mutex_lock / pthread_mutex_unlock

int pthread_mutex_unlock(pthread_mutex_t *mutex); 
  • 加锁(阻塞)和解锁。

pthread_mutex_destroy

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

注意事项

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

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

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

pthread_cond_init

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

pthread_cond_wait

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

pthread_cond_signal / pthread_cond_broadcast

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

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

设置分离状态

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

其他函数

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

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

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


完~
转载请注明出处
在这里插入图片描述