目录
- `🍑线程终止`
- `🍍线程等待`
- *多线程创建,传自己定义的对象示例代码:*
- `🍎线程的分离`
- `🍌对C++11中线程的代码实现`
🍑线程终止
如果需要只终止某个线程而不终止整个进程,可以有三种方法:
- 从线程函数return。这种方法对主线程不适用,从main函数return相当于调用exit。
- 线程可以调用
pthread_ exit
终止自己。 一个线程
可以调用pthread_ cancel
终止同一进程中的另一个线程
。
- 其中,
主线程退出==进程退出
,该进程的所有线程全部退出。 - 其中,
不能调用exit
来终止线程,因为任何一个线程调用exit都会使整个进程退出。
注意:
多线程中,任何一个线程出了异常,都会导致整个进程退出。— 多线程代码往往健壮性不好
pthread_exit函数
- 功能:线程终止
- 原型
void pthread_exit(void *value_ptr);
- 参数
- value_ptr:value_ptr不要指向一个局部变量。
- 返回值:无返回值
需要注意:
调用pthread_exit后,类似return ,即 pthread_exit((void*)100) == return (void *)100 ,pthread_exit
或者return
返回的指针所指向的内存单元必须是全局的或者是用malloc分配的
,不能在线程函数的栈上分配,因为当其它线程得到这个返回指针时线程函数已经退出了。
pthread_cancel函数
- 功能:取消一个执行中的线程
- 原型
int pthread_cancel(pthread_t thread);
- 参数
- thread:线程ID
- 返回值:成功返回0;失败返回错误码
🍍线程等待
已经退出的线程,其空间没有被释放,仍然在进程的地址空间内。
创建新的线程不会复用刚才退出线程的地址空间。
- 功能:等待线程结束
- 原型
int pthread_join(pthread_t thread, void **value_ptr);
- 参数
- thread:线程ID
- value_ptr:它指向一个指针,后者指向线程的返回值
- 返回值:成功返回0;失败返回错误码
调用该函数的线程将挂起等待,直到id为thread的线程终止(阻塞等待
)。thread线程以不同的方法终止,通过pthread_join得到的终止状态是不同的,总结如下:
- 如果thread线程通过return返回,value_ ptr所指向的单元里存放的是thread线程函数的返回值。
- 如果thread线程被别的线程调用pthread_ cancel异常终掉,value_ ptr所指向的单元里存放的是常数
PTHREAD_ CANCELED((void *) -1)
。 - 如果thread线程是自己调用pthread_exit终止的,value_ptr所指向的单元存放的是传给pthread_exit的参数。
- 如果对thread线程的终止状态不感兴趣,可以传NULL给value_ ptr参数。
注意:
pthread_join,不考虑出现异常的情况,因为线程异常,整个进程都结束了,主线程没有pthread_join的机会。
示例代码:
std::string ToHex(pthread_t tid) //将id转换为十六进制
{char buff[64];snprintf(buff, 64, "0x%lx", tid);return buff;
}void *routine(void *name)
{int cnt = 5;while (cnt--){sleep(1);std::cout << "i am new thread,name:" << (char *)name << " my tid is :" << ToHex(pthread_self()) << std::endl;}return (void*)100;
}
int main()
{pthread_t tid;pthread_create(&tid, nullptr, routine, (void *)("thread-1"));void *ret = nullptr;int n = pthread_join(tid,&ret);std::cout<<"new thread exit,n = "<< n << "get a return val:"<<(lont lont)ret<<std::endl;return 0;
}
多线程创建,传自己定义的对象示例代码:
#include <iostream>
#include <unistd.h>
#include <pthread.h>
#include <string.h>
#include <stdio.h>
#include <vector>#define threadnum 5// 线程执行的任务
class Task
{
public:Task(int x = 10, int y = 15) : _x(x), _y(y){}int excute(){return _x + _y;}~Task(){}
private:int _x;int _y;
};// 存储线程执行结果
class Result
{
public:Result(int result, std::string name) : _result(result), _name(name){}void Print(){std::cout << _name << " Task result is " << _result << std::endl;}~Result(){}
private:int _result;std::string _name;
};// 存储线程香相关数据
class threadDate : public Task
{
public:threadDate(int x, int y, const std::string &name) : _name(name), _t(Task(x, y)){}std::string name(){return _name;}int Result(){return _t.excute();}~threadDate(){}
private:std::string _name;Task _t;
};// 线程执行的函数
void *threadrun(void *argc)
{threadDate *td = static_cast<threadDate *>(argc);sleep(1);Result *Res = new Result(td->Result(), td->name());delete td;return Res;
}int main()
{// 1.创建多线程std::vector<pthread_t> threads;for (int i = 0; i < threadnum; i++){char *name = new char[64];snprintf(name, 64, "thread-%d", i + 1);// 2.参数设置threadDate *td = new threadDate(10, 15, name);pthread_t tid;pthread_create(&tid, nullptr, threadrun, td);threads.push_back(tid); //将线程的tid存放到一个vector中,便于等待}// std::vector<Result *> threadRes; //将结果的结构体放到vector中void *Res = nullptr;// 等待线程,并取task结果for (auto &tid : threads){pthread_join(tid, &Res);((Result *)Res)->Print();// threadRes.push_back((Result *)Res); //push_back进vector}// 打印结果// for (auto &Res : threadRes)// {// Res->Print();// }return 0;
}
🍎线程的分离
- 默认情况下,新创建的线程是
joinable
的,线程退出后,需要对其进行pthread_join操作
,否则无法释放资源,从而造成系统泄漏。 - 如果不关心线程的返回值,join是一种负担,这个时候,我们可以告诉系统,当线程退出时,自动释放线程资源。
int pthread_detach(pthread_t thread);
可以是线程组内其他线程对目标线程进行分离,也可以是线程自己分离:
pthread_detach(pthread_self());
joinable和分离是冲突的,一个线程不能既是joinable又是分离的。
注意:
如果你尝试对已经分离的线程(包括通过 pthread_self() 获取的当前线程)调用 pthread_join(),通常会导致未定义行为,但在大多数实现中,它会立即返回一个错误,通常是 EINVAL(表示无效参数)。分离只是不需要等待,底层依旧属于同一个进程
。
示例代码:
#include <pthread.h>
#include <unistd.h>
#include <stdio.h>
#include <string.h>
#include <iostream>
#include <cerrno>void *threadrun(void *args){//pthread_detach(pthread_self()); // 将pthread_self()线程与其他线程分离开std::string name = static_cast<char *>(args);int cnt = 5;while (cnt--){std::cout << "i am " << name << std::endl;sleep(1);}return nullptr;
}int main() {pthread_t tid;pthread_create(&tid, nullptr, threadrun, (void *)"thread -1");pthread_detach(tid); //将指定tid的线程分离int n = pthread_join(tid, nullptr);std::cout << "main thread wait return:" << n << ":" << strerror(n) << std::endl;return 0;
}
🍌对C++11中线程的代码实现
理解下面代码threadrun函数
需要设置为static
的原因,以及传入this
的原因。
- 因为pthread_create(),要求传入的函数参数为void *,如果不将函数设置为static,则参数会有一个this指针,所以需要设置static,那为什么要传入this指针呢?因为static成员函数没有this指针,但是func() 的调用需要this指针;
#ifndef __THREAD_HPP__
#define __THREAD_HPP__#include<iostream>
#include<unistd.h>
#include<functional>
#include<string>namespace ThreadModule
{template<class T>using func_t = std::function<void(T&)>;// typedef std::function<void(const T&)> func_t;template<class T>class Thread{public:Thread(func_t<T> func,T& data,std::string name):_func(func),_data(data),_name(name),_stop(true){}void execute(){_func(_data);}static void *threadrun(void *args){Thread * self = static_cast<Thread*>(args);self->execute();return nullptr;}bool start(){int n = pthread_create(&_tid,nullptr,threadrun,this); if(!n){_stop=false;return true;}else{return false;}}void stop(){if(!_stop)_stop=true;}void detach(){if(!_stop)pthread_detach(_tid);}std::string &name(){return _name;}void join(){if(!_stop)pthread_join(_tid,nullptr);}~Thread(){}private:pthread_t _tid;func_t<T> _func;T& _data;std::string _name;bool _stop;};
}#endif