Linux线程管理进阶:分离,等待、终止与C++11线程接口的封装实践

Jupiter· 2024-09-17 15:05:04 阅读 65

🍑个人主页:Jupiter.

🚀 所属专栏:Linux从入门到进阶

欢迎大家点赞收藏评论😊

在这里插入图片描述

在这里插入图片描述

目录

`🍑线程终止``🍍线程等待`*多线程创建,传自己定义的对象示例代码:*

`🍎线程的分离``🍌对C++11中线程的代码实现`


<code>🍑线程终止

如果需要只终止某个线程而不终止整个进程,可以有三种方法:

从线程函数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:线程IDvalue_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;

}

在这里插入图片描述

多线程创建,传自己定义的对象示例代码:

<code>#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




声明

本文内容仅代表作者观点,或转载于其他网站,本站不以此文作为商业用途
如有涉及侵权,请联系本站进行删除
转载本站原创文章,请注明来源及作者。