目录
1.前言
2.创建线程
pthread_create函数
3.线程终止
pthread_exit函数
pthread_cancel函数
4.线程等待
5.线程分离
1.前言
在Linux系统中,并不存在真正的线程,只有轻量级进程。所以,Linux系统只提供了操作轻量级进程的系统调用接口,并不提供直接操作线程的系统调用接口。但是,对于用户来说,用户想要对线程进行操作,只认线程相关的接口。于是,有人对轻量级进程的系统调用接口进行封装,转换成线程相关的接口语义给用户使用。
封装其实就是封装成库,这个库被叫做Linux的原生线程库:
- 与线程有关的函数构成了一个完整的系列,绝大多数函数的名字都是以“pthread_”开头。
- 要使用这些函数库,要通过引入头文<pthread.h>。
- 链接这些线程函数库时要使用编译器命令的“-lpthread”选项
2.创建线程
pthread_create函数
pthread库中创建线程的函数为pthread_create,创建出的新线程和主线程谁先运行时不确定的,由调度器说了算。
功能:用于创建一个新的线程。
函数原型:
int pthread_create(pthread_t *thread, const pthread_attr_t *attr,
void *(*start_routine) (void*), void *arg);
参数:
pthread_t *thread:
这是一个输出型参数,用于存储新线程的标识符(线程ID)。线程创建成功后,系统会将线程ID写入该指针指向的内存。原生线程库中还提供了一个让线程获取自己的线程id的方法:pthread_t pthread_self(void)
const pthread_attr_t *attr:
指向线程属性的指针,用于设置线程的属性(如栈大小、调度策略等),如果为NULL
,则使用默认属性。
void *(*start_routine) (void *)
:
线程启动后执行的函数指针。该函数必须返回
void *
并接受一个void *
类型的参数。线程从该函数的起始处开始执行,函数返回时线程终止。
void *arg:
传递给start_routine
函数的参数。如果需要传递多个参数,可以将它们封装在一个结构体中,然后传递结构体的指针。返回值:
成功:返回
0
。失败:返回错误码(非零值),常见的错误码包括:
EAGAIN
:系统资源不足,无法创建线程。
EINVAL
:线程属性无效。
EPERM
:没有权限设置调度策略或参数。
使用示例:
#include <iostream>
#include <pthread.h>
#include <unistd.h>
using namespace std;
void* handler(void* arg)
{
int cnt = 5;
while(cnt--)
{
cout << "I am a thread" << endl;
sleep(1);
}
return nullptr;
}
int main()
{
pthread_t thread_id = 0;
int ret = pthread_create(&thread_id, NULL, handler, NULL);
if(ret != 0)
{
cout << "create error" << endl;
}
sleep(6);
return 0;
}
运行结果:
当我们的代码创建出一个线程之后,新线程就会和主线程并发执行自己的代码。如果主线程先退,表示进程退出,新线程也会结束,如果新线程先结束,主线程不会直接结束,会等自己的代码运行完之后再结束。
3.线程终止
终止一个线程的方法有三种:
- return:使用return语句退出。
- pthread_exit:线程可以调用pthread_exit函数终止自己。
- pthread_cancel:一个线程可以调用pthread_cancel终止同一进程中的另一个线程。
pthread_exit函数
功能:终止当前进程的执行。
函数原型:void pthread_exit(void *retval)
参数:
void *retval
:
线程的返回值,通常是一个指向某个数据的指针。
如果不需要返回值,可以传递
NULL
。该返回值可以被其他线程通过
pthread_join
获取。返回值:该函数没有返回值。
需要注意:pthread_exit或者return返回的指针所指向的内存单元必须是全局的或者是用malloc分配的,不能在线程函数的栈上分配,因为当其它线程得到这个返回指针时,线程函数已经退出了。
使用示例:
#include <iostream>
#include <pthread.h>
#include <unistd.h>
using namespace std;
void* handler(void* arg)
{
int cnt = 3;
while(cnt--)
{
cout << "I am a thread" << endl;
sleep(1);
if(cnt == 1)
{
cout << "called pthead_exit" << endl;
pthread_exit(NULL);
}
}
return nullptr;
}
int main()
{
pthread_t thread_id = 0;
int ret = pthread_create(&thread_id, NULL, handler, NULL);
if(ret != 0)
{
cout << "create error" << endl;
}
sleep(5);
return 0;
}
运行结果:
pthread_cancel函数
功能:用于请求取消(终止)指定的线程。
函数原型:int pthread_cancel(pthread_t thread)
参数:
pthread_t thread:
目标线程的标识符(线程ID),即需要取消的线程。返回值:
成功:返回
0
。失败:返回错误码(非零值)。
使用示例:
#include <iostream>
#include <pthread.h>
#include <unistd.h>
using namespace std;
void* handler(void* arg)
{
int cnt = 10;
while(cnt--)
{
cout << "new thread say: I am runnig" << endl;
sleep(1);
}
return nullptr;
}
int main()
{
pthread_t tid = 0;
int ret = pthread_create(&tid, NULL, handler, NULL);
if(ret != 0)
{
cout << "create thread error" << endl;
}
cout << "create thread success" << endl;
sleep(5);
ret = pthread_cancel(tid);
if(ret != 0)
{
cout << "cancel thread error" << endl;
}
cout << "cancel thread success" << endl;
return 0;
}
运行结果:
4.线程等待
在进程控制中,如果一个子进程退出,父进程需要对子进程进行回收,也就是需要进行进程等待,不然就会造成僵尸进程而引发资源泄漏问题;在线程这里,一个线程退出后,主线程需要对其进行回收,不然也会产生类似的问题。
- 已经退出的线程,其空间没有被释放,仍然在进程的地址空间内。
- 创建新的线程不会复用刚才退出线程的地址空间。
pthread_join函数
功能:用于等待指定的线程终止,并获取该线程的返回值。
函数原型:int pthread_join(pthread_t thread, void **value_ptr)
参数:
thread
: 要等待的线程的标识符(pthread_t
类型)。
value_ptr
: 指向一个指针的指针,用于存储目标线程的返回值。如果不需要返回值,可以设置为NULL
。返回值:
成功时返回
0
。失败时返回一个错误码(非零值),常见的错误码包括:
ESRCH
: 没有找到与指定线程 ID 对应的线程。
EINVAL
: 线程是分离的(detached)或者已经有其他线程在等待它。
EDEADLK
: 检测到死锁(例如,线程试图等待自己)。需要注意:
调用该函数的线程将挂起等待,直到id为thread的线程终止。
thread线程以不同的方法终止,通过pthread_join得到的终止状态是不同的,总结如下:
- 1. 如果thread线程通过return返回,value_ ptr所指向的单元里存放的是thread线程函数的返回值。
- 2. 如果thread线程被别的线程调用pthread_ cancel异常终掉,value_ ptr所指向的单元里存放的是常数 —— PTHREAD_ CANCELED。
- 3. 如果thread线程是自己调用pthread_exit终止的,value_ptr所指向的单元存放的是传给pthread_exit的参数。
- 4. 如果对thread线程的终止状态不感兴趣,可以传NULL给value_ ptr参数。
使用示例:
#include <pthread.h>
#include <stdio.h>
#include <stdlib.h>
void* thread_function(void* arg) {
int* value = (int*)arg;
printf("Thread is running with value: %d\n", *value);
int* result = (int*)malloc(sizeof(int));
*result = *value * 2;
pthread_exit(result);
}
int main() {
pthread_t thread;
int value = 10;
int* retval;
if (pthread_create(&thread, NULL, thread_function, &value) != 0) {
perror("pthread_create");
exit(EXIT_FAILURE);
}
if (pthread_join(thread, (void**)&retval) != 0) {
perror("pthread_join");
exit(EXIT_FAILURE);
}
printf("Thread returned: %d\n", *retval);
free(retval);
return 0;
}
运行结果:
5.线程分离
默认情况下,新创建的线程是需要等待的,新线程退出后,需要主线程对其进行pthread_join操作,否则无法释放资源,从而造成系统资源泄漏。如果不关心线程的返回值,等待就是一种负担,这个时候,我们可以将该线程分离,当线程退出时,操作系统会自动释放被分离的线程的资源。
注意:线程分离只是主线程不用等待新线程的退出了,并不是把新线程剥离下来了。
pthread_detach函数
功能:用于将指定的线程标记为“分离状态”,分离状态的线程在终止时会自动释放其资源,而不需要其他线程调用
pthread_join
来回收资源。函数原型:int pthread_detach(pthread_t thread)
参数:
thread:
要分离的线程的标识符(pthread_t
类型)返回值:
成功时返回
0
。失败时返回一个错误码(非零值),常见的错误码包括:
ESRCH
: 没有找到与指定线程 ID 对应的线程。
EINVAL
: 线程已经是分离状态,或者线程已经终止。注意:
- 一旦线程被分离,就不能再调用
pthread_join
来等待它,否则会导致未定义行为- 线程分离,可以是线程组内其他线程对目标线程进行分离,也可以是线程自己将自己分离。
使用示例:
#include <pthread.h>
#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
void* thread_function(void* arg) {
int* value = (int*)arg;
printf("Thread is running with value: %d\n", *value);
sleep(2); // 模拟线程执行一些任务
printf("Thread is exiting\n");
pthread_exit(NULL);
}
int main() {
pthread_t thread;
int value = 10;
if (pthread_create(&thread, NULL, thread_function, &value) != 0) {
perror("pthread_create");
exit(EXIT_FAILURE);
}
// 分离线程
if (pthread_detach(thread) != 0) {
perror("pthread_detach");
exit(EXIT_FAILURE);
}
printf("Main thread continues to run...\n");
sleep(3); // 确保分离的线程有足够时间完成
return 0;
}
运行结果: