目录
3.线程的终止 (pthread_exit / pthread_cancel)
0.前言
线程的创建,终止,等待,分离
1.pthread库
Linux中有线程吗?没有,只有轻量级进程--(就是线程)。因此Linux下的系统调用只会给用户提供创建轻量级进程的接口,这些接口需要被pthread库进行封装,按照线程的接口提供给用户,用户通过这些接口来创建,终止,等待,分离线程。所以我们称Linux的线程为用户级线程,windows的线程为内核级线程。
![]()
2.关于控制线程的接口
2.1.创建线程(pthread_create)
引入接口:pthread_create,用于创建一个新线程
参数说明
pthread_t *thread
:这是一个指向pthread_t
类型的指针,用于存储新创建的线程的标识符。通过这个标识符,你可以引用或操作这个线程。const pthread_attr_t *attr
:这是一个指向pthread_attr_t
类型的指针,用于设置线程的属性,如堆栈大小、调度策略等。如果传递NULL
,则使用默认属性。void *(*start_routine) (void *)
:这是新线程将要执行的函数的指针。该函数必须接受一个void *
类型的参数并返回一个void *
类型的值。这个函数的参数arg
将被传递给新线程。(输入一个函数的地址)void *arg
:这是传递给start_routine
函数的参数。如果成功,
pthread_create
返回0
;如果失败,则返回错误码。
2.2.线程等待(pthread_join)
引入接口:pthread_join
参数说明
pthread_t thread
:这是要等待的线程的标识符(ID),该标识符是由pthread_create
函数返回的。void **retval
:这是一个指向void *
指针的指针,用于接收被等待线程的返回值。如果被等待的线程调用了pthread_exit
并传递了一个返回值,或者简单地返回了一个值(对于从void*
返回类型的线程函数),那么这个值就可以通过这个参数返回给等待的线程。如果对这个返回值不感兴趣,可以传递NULL
。如果成功,
pthread_join
返回0
;如果失败,则返回错误码。
代码示例1:
线程的创建和等待:
#include <iostream> #include <string> #include <pthread.h> #include <unistd.h> void *threadrun(void *args) { int cnt =10; while(cnt) { std::cout<<"new thread run ...,cnt: "<<cnt--<<std::endl; sleep(1); } return nullptr; } int main() { pthread_t tid; int n = pthread_create(&tid,nullptr,threadrun,(void*)"thread 1"); std::cout<<"main thread join begin..."<<std::endl; n= pthread_join(tid,nullptr); if(n==0) { std::cout<<"main thread wait success"<<std::endl; } return 0; }
***一些问题***
问题1:mian和new线程谁先运行?不确定
问题2:我们期望谁最后退出?main thread,如何来保证呢?
join来保证,不join呢?会造成类似僵尸进程的问题
问题3:tid是什么样子的?
代码:以16进制的形式打印出来
std::string PrintToHex(pthread_t &tid) { char buffer[64]; snprintf(buffer, sizeof(buffer), "0x%lx", tid); return buffer; } std::string tid_str = PrintToHex(tid); // 我们按照16进行打印出来 std::cout << "tid : " << tid_str << std::endl;
这个线程id是一个虚拟地址,后面再谈
问题4:全面看待线程函数传参,它可以传任意类型,当然也可以传类对象的地址,这意味着我们可以给线程传递多个参数,多种方法了
class ThreadData { public: std::string name; int num; }; void *threadrun(void *args) { //静态强转 ThreadData *td = static_cast<ThreadData*>(args); int cnt =10; while(cnt) { std::cout << td->name << " run ..." <<"num is: "<<td->num<< ", cnt: " << cnt-- << std::endl; sleep(1); } return nullptr; } int main() { ThreadData *td=new ThreadData(); td->name ="thread-1"; td->num = 1; int n = pthread_create(&tid,nullptr,threadrun,(void*)&td); }
传类对象的时候最好是在堆上开辟,这样多个线程之间就不会互相干扰。
问题5:pthread_create第三个参数的返回值,该返回值是void*类型的,如果主线程想要获取线程的返回值,可以通过join函数获取(在线程没出错的情况下是能获取到的,如果某一个线程出错,主线程也是会跟着崩掉,因为线程出错误,是直接给整个进程发信号的,导致整个进程都挂掉了)
代码示例:返回一个类对象
#include <iostream> #include <string> #include <thread> #include <stdlib.h> #include <pthread.h> #include <unistd.h> class ThreadData { public: int Excute() { return x + y; } public: std::string name; int x; int y; // other }; class ThreadResult { public: std::string print() { return std::to_string(x) + "+" + std::to_string(y) + "=" + std::to_string(result); } public: int x; int y; int result; }; std::string PrintToHex(pthread_t &tid) { char buffer[64]; snprintf(buffer, sizeof(buffer), "0x%lx", tid); return buffer; } void *threadRun(void *args) { ThreadData *td = static_cast<ThreadData*>(args); // (ThreadData*)args ThreadResult *result = new ThreadResult(); int cnt = 10; while(cnt) { sleep(3); std::cout << td->name << " run ..." << ", cnt: " << cnt-- << std::endl; result->result = td->Excute(); result->x = td->x; result->y = td->y; break;//跑一次退出 } delete td; return (void*)result; } int main() { pthread_t tid; ThreadData *td=new ThreadData(); td->name="thread-1"; td->x=10; td->y=20; int n = pthread_create(&tid, nullptr, threadRun, td); std::string tid_str = PrintToHex(tid); // 我们按照16进行打印出来 std::cout << "tid : " << tid_str << std::endl; std::cout<<"main thread join begin..."<<std::endl; ThreadResult *result = nullptr; // 开辟了空间的!!! n = pthread_join(tid, (void**)&result); if(n == 0) { std::cout << "main thread wait success, new thread exit code: " << result->print() << std::endl; } sleep(10); return 0; }
2. 3.创建多线程
下面是一段示例:
初步:创建线程id和线程name,保存所有线程的id信息,最后主线程回收每个线程
#include <iostream> #include <string> #include <vector> #include <thread> #include <stdlib.h> #include <pthread.h> #include <unistd.h> const int num = 10; std::string PrintToHex(pthread_t &tid) { char buffer[64]; snprintf(buffer, sizeof(buffer), "0x%lx", tid); return buffer; } void *threadrun(void *args) { std::string name = static_cast<const char*>(args); while(true) { std::cout << name << " is running" << std::endl; sleep(1); break; } return args; } int main() { std::vector<pthread_t> tids; for(int i = 0; i < num; i++) { // 1. 有线程的id pthread_t tid; // 2. 线程的名字 char *name = new char[128]; snprintf(name, 128, "thread-%d", i+1); pthread_create(&tid, nullptr, threadrun, /*线程的名字*/name); //3.保存所有线程id tids.push_back(tid); } for(auto tid:tids) { void*name=nullptr; pthread_join(tid,&name); std::cout<<(const char*)name<<"quit..."<<std::endl; delete (const char*)name; } }
我们用vector储存线程id集
3.线程的终止 (pthread_exit / pthread_cancel)
对于新线程来说,线程终止,函数return;main函数结束,主线程结束,表示整个进程结束!
关于exit:专门用来终止进程的,不能用来终止线程!任意一个线程调用exit都表示进程终止!如果你想让一个线程马上终止,这里就要用到第三个接口:pthread_exit
参数:
retval
:这是一个指向任意数据的指针,该数据将被线程的终止状态所使用,并且可以被其他线程通过调用pthread_join
来访问。当然你还可以使用接口:pthread_cancel取消一个线程
参数:
thread
:要发送取消请求的线程标识符(pthread_t 类型)。代码示例:
#include <iostream> #include <string> #include <vector> #include <thread> #include <stdlib.h> #include <pthread.h> #include <unistd.h> const int num = 10; std::string PrintToHex(pthread_t &tid) { char buffer[64]; snprintf(buffer, sizeof(buffer), "0x%lx", tid); return buffer; } void *threadrun(void *args) { std::string name = static_cast<const char*>(args); while(true) { std::cout << name << " is running" << std::endl; sleep(1); break; } //return args; pthread_exit(args); } int main() { std::vector<pthread_t> tids; for(int i = 0; i < num; i++) { // 1. 有线程的id pthread_t tid; // 2. 线程的名字 char *name = new char[128]; snprintf(name, 128, "thread-%d", i+1); pthread_create(&tid, nullptr, threadrun, /*线程的名字*/name); //3.保存所有线程id tids.push_back(tid); } for(auto tid:tids) { void*name=nullptr; pthread_join(tid,&name); std::cout<<(const char*)name<<"quit..."<<std::endl; delete (const char*)name; } sleep(100); }
在主线程未退出的情况下,其它线程成功退出了。
线程取消,退出结果为-1; #define PTHREAD_CANCELED ((void *) -1)
#include <iostream> #include <string> #include <vector> #include <thread> #include <stdlib.h> #include <pthread.h> #include <unistd.h> const int num = 10; std::string PrintToHex(pthread_t &tid) { char buffer[64]; snprintf(buffer, sizeof(buffer), "0x%lx", tid); return buffer; } void *threadrun(void *args) { std::string name = static_cast<const char*>(args); while(true) { std::cout << name << " is running" << std::endl; sleep(1); } } int main() { std::vector<pthread_t> tids; for(int i = 0; i < num; i++) { // 1. 有线程的id pthread_t tid; // 2. 线程的名字 char *name = new char[128]; snprintf(name, 128, "thread-%d", i+1); pthread_create(&tid, nullptr, threadrun, /*线程的名字*/name); //3.保存所有线程id tids.push_back(tid); } sleep(5); for(auto tid : tids) { pthread_cancel(tid); // 取消 std::cout << "cancel: " << PrintToHex(tid) << std::endl; void *result = nullptr; // 线程被取消线程的退出结果是:-1 #define PTHREAD_CANCELED ((void *) -1) pthread_join(tid, &result); std::cout << (long long int)result << " quit..." << std::endl; } sleep(100); }
总结:
新线程如何终止?
1. 线程函数 return
2. pthread_exit
3. main thread call pthread_cancel, 新线程退出结果是-1
4.线程分离 (pthread_detach)
线程分离的是将线程与创建它的进程(或主线程)的终止状态分离。当一个线程被分离后,它依然属于进程内部,但它不再需要被其他线程显式地等待(通过
pthread_join
)来释放其资源。当分离的线程终止时,它的所有资源会自动被释放回系统,无需其他线程的干预。
参数
thread
:要分离的线程的标识符(pthread_t 类型)。返回值
- 成功时返回 0。
- 失败时返回错误号。
一个线程被创建,默认是joinable,必须要被join的;如果一个线程被分离,线程的工作状态分离状态,不需要/不能被join的。
这里我们还需要借助一个接口:pthread_self,一调用就是获取自己的线程id
新线程分离主线程
代码示例:一旦分离主线程就不能等待了,如果等待会发生什么?这里我们看一下分离且join后,join的返回值
我们发现返回值为:22,这说明主线程以等待就直接出错了。所以主线程无需等待,主线程可以做自己的事情了。如果在线程分离的情况下,且主线程没有做等待,新线程出错了,整个进程也是直接挂掉的,因为它还是在进程内部。
#include <iostream> #include <string> #include <vector> #include <thread> #include <stdlib.h> #include <pthread.h> #include <unistd.h> const int num = 10; std::string PrintToHex(pthread_t &tid) { char buffer[64]; snprintf(buffer, sizeof(buffer), "0x%lx", tid); return buffer; } void *threadrun(void *args) { pthread_detach(pthread_self()); std::string name = static_cast<const char*>(args); while(true) { std::cout << name << " is running" << std::endl; sleep(1); } pthread_exit(args); } int main() { std::vector<pthread_t> tids; for(int i = 0; i < num; i++) { // 1. 有线程的id pthread_t tid; // 2. 线程的名字 char *name = new char[128]; snprintf(name, 128, "thread-%d", i+1); pthread_create(&tid, nullptr, threadrun, /*线程的名字*/name); //3.保存所有线程id tids.push_back(tid); } sleep(5); for(auto tid : tids) { std::cout << "cancel: " << PrintToHex(tid) << std::endl; void *result = nullptr; // 线程被取消线程的退出结果是:-1 #define PTHREAD_CANCELED ((void *) -1) int n = pthread_join(tid, &result); std::cout << (long long int)result << " quit...n :" <<n<< std::endl; } sleep(100); }
5.C++ 11中的多线程
C++11在Linux中使用多线程,编译时也是要链接pthread库,因为C++11中的多线程本质,就是对原生线程库接口的封装!!!
#include <iostream> #include <string> #include <vector> #include <thread> #include <stdlib.h> #include <pthread.h> #include <unistd.h> void threadrun(std::string name, int num) { while(num) { std::cout << name << " num : " << num<< std::endl; num--; sleep(1); } } int main() { std::string name = "thread-1"; std::thread mythread(threadrun, std::move(name), 10); while(true) { std::cout << "main thhread..." << std::endl; sleep(1); } mythread.join(); return 0; }