欢迎来到尧图网

客户服务 关于我们

您的位置:首页 > 财经 > 金融 > 初识Linux · 线程控制(2)

初识Linux · 线程控制(2)

2025/9/19 0:45:47 来源:https://blog.csdn.net/2301_79697943/article/details/144093736  浏览:    关键词:初识Linux · 线程控制(2)

目录

前言:

N个子问题

问题六:如何创建多线程呢?

问题七:新线程如何终止?

问题八:可不可以让线程不用join,让它执行完自己就退出了?

模拟thread


前言:

在前面Linux的线程控制1中,我们介绍了如下的几个问题:主线程和子线程的执行顺序,主线程先退出还是子线程先退出,什么是tid,全面看待线程函数传参,全面看待线程函数的返回值。

以上是N个子问题的部分问题,本文,我们要学习N个子问题的剩余问题以及我们要简单的模拟实现一下线程。

那么话不多说,我们直接进入主题吧!


N个子问题

问题六:如何创建多线程呢?

我们可以使用函数pthread_create创建线程,同样可以利用它创建多个线程,并且我们是不需要担心函数pthread_create创建的子线程会再次创建子线程的,因为子线程执行的方法是我们给定了的,所以在主线程里面我们就尽情的创建即可。


void* threadRun(void* args)
{std::string name = static_cast<const char*>(args);std::cout << "This is new thread, it's name is " << name << std::endl;sleep(3);return nullptr;
}int main()
{// 问题六:如何创建多线程?std::vector<pthread_t> v;for(int i = 0;i < 10; i++){pthread_t tid;char* name = new char[128];snprintf(name,128,"thread - %d", i);pthread_create(&tid, nullptr, threadRun, (void*)name);v.emplace_back(tid);}for(auto e : v){pthread_join(e, nullptr);}return 0;
}

我们为了方便起见,将创建的子线程统一防在了顺序表里面,放在里面之后,就,欻欻欻的创建即可,名字这里稍微有点点麻烦,我们使用snprintf简单对name处理了一下,然后在命令行中使用ps -aL查看即可:

550b702014764602978ffc23989ca376.png

3093458118a84da7b958c76459622f6e.png

这个问题还是比较简单的,无非借助了个容器,其实如果我们不想要收集线程的id的话,不用数组也可以,只是后面操作没有那么顺畅罢了。

问题七:新线程如何终止?

我们先不管是否终止,我们就利用上面的代码,简单看一下现象:

void* threadRun(void* args)
{std::string name = static_cast<const char*>(args);std::cout << "This is new thread, it's name is " << name << std::endl;// sleep(3);exit(1);// return nullptr;
}

我们将返回值简单替换成了exit,直接看现象:

ab58fd58dee5491585583e3f5f1fb854.png

第一点,我们明明创建了10个线程,可是为什么打印出来的只有这么几个呢?

第二点,在ps -aL打印出来的,我们甚至看不到有对应的线程出现了。

那么这里不废话,直接给结论,对于exit来说是用来终止进程的,而对于线程,也就是Linux中的轻量级进程来说,使用该函数是会直接报错的,所以如何终止一个特定的线程呢?

我们使用到的函数是pthread_exit:

31e9b9ae0f754c3b82500da098153dfd.png

其中的参数表示的是向其他进程传递线程的退出结果或状态。

void* threadRun(void* args)
{std::string name = static_cast<const char*>(args);std::cout << "This is new thread, it's name is " << name << std::endl;// exit(1);sleep(3);pthread_exit(args);// return nullptr;
}

如果我不想传递任何信息的话,直接设置为空就可以了:

f193f02fd7a546ed8be202761c2b744a.png

此时就很beautiful了。

当然了,还可以使用函数pthread_cancel,这里就不演示了,同学们自己尝试。

问题八:可不可以让线程不用join,让它执行完自己就退出了?

对于这个问题来说,当然是可以的,但是我们需要注意的点是一个线程被创建的时候,默认是joinable的,那么如果我们想要完成上述操作,需要用到一个函数:

87463384d40f4f6f891b1d509ded359e.png

detach,熟悉吧?在进程间通信的时候我们夜也介绍过,在这里,是线程脱离的意思。

打个比方,对于一个家庭来说,默认家庭成员是绑定在一起的,但是有一天你和你家人吵架了,你一生气,出去自己找了个房子居住,此时你和家庭脱离了,但是并不是真正的脱离,为什么不是真正的脱离呢?因为要是真出了什么事,你们还是的共同解决。

线程这里一样,即便一个线程被分离出去了,不被join,它出问题了的话,其他的所有线程都要崩,这里还有一个小小的前提,线程被分离的时候,该线程一定要是存在的。

第二,它的参数是线程的id,那么我们可以通过一个函数直接得到:

383cad2e9c304643b2fd040ac9320ca7.png

所以:

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(3);int *p = nullptr;*p = 100;break;}pthread_exit(args); // 专门终止一个线程的!
}

至于测试的话,大家可以自行试试哦。


模拟thread

对于线程的N个子问题我们已经介绍完了,在模拟实现thread之前,我们不妨思考一下,在Linux这个系统里面我们可以使用上面的方式创建,在Windows呢?Mac呢?

这时候,其实是call back文件系统了,对于系统调用,不具有可移植性,所以有了高级语言自己封装的线程等,比如C++的:

6796343539c44bad929a31011a93107a.png

直接就是将thread封装为了一个类。

它的本质也是封装了底层的thread,现在我们就来简单模拟实现一下,模拟实现线程我们的大体思路得有吧?

那么对于线程来说,我们应该开始线程,终止线程,join线程,线程执行对应函数。

    class Thread{public:void Excute(){std::cout << _name << " is running" << std::endl;_isrunning = true;_func(_name);_isrunning = false;}public:Thread(const std::string &name, func_t func):_name(name), _func(func){std::cout << "create " << name << " done" << std::endl;}static void *ThreadRoutine(void *args){Thread *self = static_cast<Thread*>(args); self->Excute();return nullptr;}bool Start(){int n = ::pthread_create(&_tid, nullptr, ThreadRoutine, this);if(n != 0) return false;return true;}std::string Status(){if(_isrunning) return "running";else return "sleep";}void Stop(){if(_isrunning){::pthread_cancel(_tid);_isrunning = false;std::cout << _name << " Stop" << std::endl;}}void Join(){::pthread_join(_tid, nullptr);std::cout << _name << " Joined" << std::endl;}std::string Name(){return _name;}~Thread(){}private:std::string _name;pthread_t _tid;bool _isrunning;func_t _func;};

模拟实现线程的时候,唯一一个需要注意的点是,实现回调函数的时候,因为类的特性,类成员函数有一个this指针,而函数应该只有一个void类型的指针,所以我们需要将函数设置为静态的,这样就没有对应的this指针了。

那么执行函数的时候,我们将this指针传递过去调用即可。

以上是线程控制的全部内容。


感谢阅读!

 

版权声明:

本网仅为发布的内容提供存储空间,不对发表、转载的内容提供任何形式的保证。凡本网注明“来源:XXX网络”的作品,均转载自其它媒体,著作权归作者所有,商业转载请联系作者获得授权,非商业转载请注明出处。

我们尊重并感谢每一位作者,均已注明文章来源和作者。如因作品内容、版权或其它问题,请及时与我们联系,联系邮箱:809451989@qq.com,投稿邮箱:809451989@qq.com

热搜词