目录

Linux操作系统6-线程2线程的创建,终止,等待与退出

Linux操作系统6- 线程2(线程的创建,终止,等待与退出)

上篇文章:

本篇Gitee仓库:[myLerningCode/l28 · 橘子真甜/Linux操作系统与网络编程学习 - 码云 - 开源中国 (gitee.com)]( learning/tree/master/myLerningCode/l28 “myLerningCode/l28 · 橘子真甜/Linux操作系统与网络编程学习 - 码云 - 开源中国 (gitee.com)”) 进程:承担操作系统分配资源的基本单位 线程:CPU调度的基本单位,是进程内的执行流。在Linux中,使用轻量级进程模拟线程 本文的线程操作都是POSIX线程控制库。链接这些线程库需要在编译的时候加上 -lpthread


一. 线程创建pthread_create

调用接口原型 //所需头文件 #include //用于创建一个线程并执行对应函数 int pthread_create(pthread_t* thread, const pthread_attr_t attr, void(start_rountine)(void), void* args); // thread 输入输出型参数,表示创造线程的tid // attr 用于控制线程的属性,一般设置为nullptr // start_rountine 一个函数指针,表示要线程执行的函数 // args 一个void参数,线程创建后,将这个参数传递给start_routine的参数列表 //成功返回0,失败返回-1,并且设置错误码 args参数是void类型的,所以我们能够传递任意参数让其接收。 测试代码: 我们使用vector保存线程的tid,同时创建一批线程,并且通过arg参数传递一个string类型作为线程的名字 #include #include #include #include int gnum = 5; void *start_routine(void *args) { std::string name = static_cast(args); while (true) { std::cout « “我是新线程,我的名字是” « name « “pid为:” « getpid() « std::endl; sleep(1); } } int main() { std::vector tids(gnum); for (int i = 0; i < tids.size(); ++i) { std::string name = “thread” + std::to_string(i); pthread_create(&tids[i], nullptr, start_routine, (void *)name.c_str()); } while (true) { std::cout « “我是主线程,我的pid为:” « getpid() « std::endl; sleep(1); } return 0; } 测试结果如下: https://i-blog.csdnimg.cn/direct/c79b2fd5140d4b838de48e0f3f7a808e.png 可以看到,我们创建了4个新线程,它们的pid相同。但是它们的名字为何没有0,2和3呢? 这是因为我们的name是同一个name,销毁后,线程仍访问这块销毁空间。 所以这里我们需要定义一个结构体来保存数据。 示例代码: #include #include #include #include int gnum = 5; struct ThreadData { pthread_t tid; std::string name; }; void *start_routine(void *args) { ThreadData *td = static_cast(args); while (true) { std::cout « “我是新线程,我的名字是:” « td->name « " pid为:" « getpid() « std::endl; sleep(1); } } int main() { std::vector tids(gnum); for (int i = 0; i < tids.size(); ++i) { // 在堆中创建数据 ThreadData *td = new ThreadData(); td->name = “Thread " + std::to_string(i); pthread_create(&td->tid, nullptr, start_routine, (void *)td); tids[i] = td; } while (true) { std::cout « “我是主线程,我的pid为:” « getpid() « " " « std::endl; sleep(1); } return 0; } 测试结果如下: https://i-blog.csdnimg.cn/direct/26a8660785e04b1ba2c96279878643c5.png 如果在线程的执行流中定义变量,这些变量是线程独有的 修改start_routine void *start_routine(void *args) { int cnt = 10; ThreadData *td = static_cast(args); while (true) { std::cout « “我是新线程,我的名字是:” « td->name « “cnt:” « cnt– « “cnt 地址为:” « &cnt « std::endl; sleep(1); } } 运行结果如下:可以看到,每一个线程中的cnt的地址都是不一样的。这说明每一个线程都有自己独立的栈资源 https://i-blog.csdnimg.cn/direct/48e25542e58f475eb390bf856a25414a.png

二. 线程的终止

如果线程调用exit()会导致整个线程终止。 测试代码如下:在cnt为5的时候,线程调用exit进行终止 #include #include #include #include int gnum = 5; struct ThreadData { pthread_t tid; std::string name; }; void *start_routine(void *args) { int cnt = 10; ThreadData *td = static_cast(args); while (true) { if (cnt == 5) exit(1); std::cout « “我是新线程,我的名字是:” « td->name « " cnt:” « cnt– « " cnt 地址为:" « &cnt « std::endl; sleep(1); } } int main() { std::vector tids(gnum); for (int i = 0; i < tids.size(); ++i) { // 在堆中创建数据 ThreadData *td = new ThreadData(); td->name = “Thread " + std::to_string(i); pthread_create(&td->tid, nullptr, start_routine, (void *)td); tids[i] = td; } while (true) { std::cout « “我是主线程,我的pid为:” « getpid() « " " « std::endl; sleep(1); } return 0; } 运行结果如下: https://i-blog.csdnimg.cn/direct/17cae42102884288ad147c4e44bc249f.png 可以看到,线程调用exit会导致整个进程退出。exit是用于进程终止,而不是用于某一个线程的终止。 线程终止是当线程的函数执行完毕就会自动退出。 修改start_routine:当cnt为0的时候,退出循环。以返回的形式终止线程 void *start_routine(void *args) { int cnt = 10; ThreadData *td = static_cast(args); while (cnt–) { std::cout « “我是新线程,我的名字是:” « td->name « " cnt:” « cnt « " cnt 地址为:" « &cnt « std::endl; sleep(1); } return nullptr; } 运行结果如下: https://i-blog.csdnimg.cn/direct/880a982a8e1a4e27918bb28753f7dfc4.png

2.1 pthread_exit

可以通过pthread_exit退出线程。效果与return是一样的 //函数原型 void pthread_exit(void* retval) pthread_exit的效果与函数内使用return的效果是一样的,这里就不过多解释

三. 线程的等待与退出

与进程等待一样,线程也需要等待。如果不等待线程,线程对应的pcb可能不会被释放。就会造成类似僵尸进程一样的资源泄露。

线程等待的作用

1 等待线程退出的资源与信息(通过 return 的返回值获取)

2 回收线程的pcb与内核资源防止资源泄露

****当然我们可以不获取线程退出的信息,但是必须收回线程的资源

3.1 pthread_join

pthread_join用于主线程等待新线程的退出。通过输入输出型参数,可以获取主线程退出的信息和需要传递给主线程的资源。同时会回收新线程的pcb和内核资源。 //所需头文件 #include //函数原型 int pthread_join(pthread_t thread, void** retval) //参数说明 thread:需要等待的线程 retval:输入输出型参数,通过指针的方法来获取我们的退出值 测试代码:主线程一次性等待所有的新线程 #include #include #include #include int gnum = 5; struct ThreadData { pthread_t tid; std::string name; }; void *start_routine(void *args) { int cnt = 10; ThreadData *td = static_cast(args); while (cnt–) { std::cout « “我是新线程,我的名字是:” « td->name « " cnt:" « cnt « " cnt 地址为:" « &cnt « std::endl; sleep(1); } return nullptr; } int main() { std::vector tds(gnum); for (int i = 0; i < tds.size(); ++i) { // 在堆中创建数据 ThreadData *td = new ThreadData(); td->name = “Thread " + std::to_string(i); pthread_create(&td->tid, nullptr, start_routine, (void *)td); tds[i] = td; } for (const auto &td : tds) { int n = pthread_join(td->tid, nullptr); // 使用bullptr表示不关心线程退出的状态与信息 if (n != 0) std::cout « “线程退出失败” « std::endl; std::cout « “主线程成功等待新线程:” « td->name « std::endl; } while (true) { std::cout « “我是主线程,我的pid为:” « getpid() « " " « std::endl; sleep(1); } return 0; } 测试结果如下:可以看到,主线程成功等待了所有等待新线程 https://i-blog.csdnimg.cn/direct/e538462a3d334364ac22b7a523323767.png

3.2 线程退出信息

无论是return还是调用pthread_join退出的信息都是 void*retval。pthread_join通过输入输出型参数 voiddretval来获取退出信息。** 测试代码如下: #include #include #include #include int gnum = 5; struct ThreadData { pthread_t tid; std::string name; }; struct ThreadReturn { int exit_code; int exit_result; }; void *start_routine(void *args) { int cnt = 10; ThreadData *td = static_cast(args); while (cnt–) { std::cout « “我是新线程,我的名字是:” « td->name « " cnt:” « cnt « " cnt 地址为:" « &cnt « std::endl; sleep(1); } ThreadReturn *tr = new ThreadReturn(); // 堆中创建的数据才能正常返回 tr->exit_code = 0; tr->exit_result = td->tid; return (void *)tr; // 退出线程的信息结构体 } int main() { std::vector tds(gnum); for (int i = 0; i < tds.size(); ++i) { // 在堆中创建数据 ThreadData *td = new ThreadData(); td->name = “Thread " + std::to_string(i); pthread_create(&td->tid, nullptr, start_routine, (void *)td); tds[i] = td; } for (const auto &td : tds) { ThreadReturn *tr; // 用于获取线程退出的信息 int n = pthread_join(td->tid, (void **)&tr); // 使用bullptr表示不关心线程退出的状态与信息 if (n != 0) std::cout « “线程退出失败” « std::endl; std::cout « “主线程成功等待新线程:” « td->name; std::cout « " 该线程退出大队信息为 " « “exitcode:” « tr->exit_code « " exit_result:” « tr->exit_result « std::endl; } while (true) { std::cout « “我是主线程,我的pid为:” « getpid() « " " « std::endl; sleep(1); } return 0; } 测试结果如下: https://i-blog.csdnimg.cn/direct/66c4e53ab2e64c1e8a587c5384b6ea14.png

在进程退出中,我们会关心进程的退出码和退出信号。

而线程不用关心异常信号,因为一旦出现异常信号整个进程都会退出。