线程系列:
线程的认识:讲解线程的概念和线程的根本控制
线程的分离
线程分离是指将一个线程从主线程中分离出来,使其能够独立运行。当一个线程被设置为分离状态时,它结束时系统会自动回收其资源,而不必要其他线程利用pthread_join()函数来等候其结束并手动回收资源。
设置线程分离的方法:
利用pthread_detach()函数:在线程创建后,可以通过调用pthread_detach()函数来将线程设置为分离状态。这个函数黑白阻塞式的,即调用后不会阻塞当前线程的执行。
在创建线程时设置分离属性:另一种方法是在创建线程时,通过pthread_create()函数的第二个参数(线程属性)来设置线程为分离状态。这种方法在创建线程时即指定了其分离属性,效率相对较高。
- void* threadrun(void* args)
- {
- string name = static_cast<const char *>(args);
- while(true)
- {
- sleep(1);
- cout<<"this is new thread:"<<name<<endl;
- }
- }
- int main()
- {
- pthread_t tid;
- pthread_create(&tid, nullptr, threadrun, (void *)"thread 1");
- cout << "main thread wait block" << std::endl;
- pthread_join(tid, nullptr);
- cout << "main thread wait return"<<endl;
- }
复制代码
利用分离函数后:
再加个有限时间的循环看看:
对线程分离明白:虽然新线程与主线程已经分离了,但它们仍然是同一历程中的执行流,如果步伐利用时出现异常时(新线程或者主线程),那么两个步伐都会终止;或者说主线程结束了,实际上就代表历程结束了;所以线程的分离仍然是在历程中进行的,受历程的影响;
何时利用:当线程完成任务后不必要与其结果交付时;当线程在后台运行且不必要与主线程进行同步进行时;
注意:分离线程无法重新连接!而可连接线程可以分离,当只有在尚未开始运行之前。
明白线程库的地点关系
线程栈
线程栈是与线程紧密相关的内存区域,用于存储线程的局部变量、函数调用的返回地点以及线程的执行上下文等信息。每个线程都有自己独立的栈空间,这保证了线程之间的数据是隔离的,从而制止数据竞争和线程安全问题。
- #include<iostream>
- using namespace std;
- #include<pthread.h>
- #include<unistd.h>
- void *threadrun1(void *args)
- {
- std::string name = static_cast<const char *>(args);
- int g_val=100;
- while(true)
- {
- sleep(1);
- printf("%s, g_val: %lu, &g_val: %p\n", name.c_str(), g_val--, &g_val);
- }
- return nullptr;
- }
- void *threadrun2(void *args)
- {
- std::string name = static_cast<const char *>(args);
- int g_val=100;
- while(true)
- {
- printf("%s, g_val: %lu, &g_val: %p\n", name.c_str(), g_val--, &g_val);
- sleep(1);
- }
- return nullptr;
- }
- int main()
- {
- pthread_t tid1;
- pthread_t tid2;
- pthread_create(&tid1, nullptr, threadrun1, (void *)"thread 1");
- pthread_create(&tid2, nullptr, threadrun2, (void *)"thread 2");
- pthread_join(tid1, nullptr);
- pthread_join(tid2, nullptr);
- }
复制代码 通过两个新线程都创建一个局部变量(变量名相同),比力它们的地点;
可以看到g_val在各自线程是不一样的,地点也是差别的;
线程局部存储(TLS)
线程局部存储(TLS)是一种机制,允许每个线程拥有自己的私有数据副本,纵然差别线程执行相同的代码,TLS变量与通例全局变量是差别的,因为每个线程堆TLS变量的访问都是独立的。
一样平常实用于:
- 线程特定数据:当某些数据只对特定线程有意义,并且必要在线程内保持状态时,可以利用线程局部存储。
- 全局状态隔离:通过将全局状态分离为每个线程的私有副本,可以进步并发性能,制止线程间的数据竞争。
- 线程上下文保存:线程局部存储也可用于保存当前执行线程的上下文信息,如用户身份验证信息、数据库连接等。
注意:
线程局部存储变量通常只能用于具有静态或线程存储期的变量,不能用于自动或动态分配的变量。利用线程局部存储时必要谨慎管理内存,制止内存泄漏或无效访问等问题。
线程的封装
线程的封装通常指的是将线程的创建、执行、同步、资源管理等逻辑封装到一个类或对象中,以便更好地组织代码,进步代码的可读性和可维护性。
封装线程可以隐藏线程的复杂性,使得其他部分的代码可以更加简便地与线程进行交互。
下面看具体代码:
Thread.hpp:对线程的封装
- #ifndef __THREAD_HPP__
- #define __THREAD_HPP__
- #include<iostream>
- #include<string>
- #include<pthread.h>
- #include<functional>
- #include<unistd.h>
- using namespace std;
- namespace ThreadMdule
- {
- //通过模板类可调用一切任何对象
- template<typename T>
- using func_t = std::function<void(T&)>;
- template<typename T>
- class Thread
- {
- public:
- void Excute()
- {
- _func(_data);
- }
- Thread(func_t<T> func, T data, const std::string &name="none-name")
- : _func(func), _data(data), _threadname(name), _stop(true)
- {}
- static void* threadroutine(void* args)
- {
- Thread<T>* self=static_cast<Thread<T>*>(args);
- self->Excute();
- return nullptr;
- }
-
- bool start()
- {
- int n=pthread_create(&_tid,nullptr,threadroutine,this);
- if(!n)
- {
- _stop = false;
- return true;
- }
- else
- {
- return false;
- }
- }
- void Detach()
- {
- if(!_stop)
- {
- pthread_detach(_tid);
- }
- }
- void Join()
- {
- if(!_stop)
- {
- pthread_join(_tid,nullptr);
- }
- }
- string name()
- {
- return _threadname;
- }
- void Stop()
- {
- _stop = true;
- }
- ~Thread() {}
- private:
- pthread_t _tid;
- std::string _threadname;
- T _data;
- func_t<T> _func;
- bool _stop;
- };
- }
- #endif
复制代码 线程类中包罗了:线程名,数据,调用函数指针等;
通过start()函数来创建新线程:用到了函数threadroutinue,在函数中将函数成员_func(也就是具体函数的指针)利用了起来,就表示新线程的创建利用;
主函数的调用:
- void print(int &cnt)
- {
- while (cnt)
- {
- std::cout << "hello I am myself thread, cnt: " << cnt-- << std::endl;
- sleep(1);
- }
- }
- const int num=3;
- int main()
- {
- vector<Thread<int>> threads;
- //创建新线程
- for(int i=0;i<num;i++)
- {
- string name="thread"+to_string(i + 1);
- threads.emplace_back(print,3,name);
- }
- //启动进程
- for(auto& thread:threads)
- {
- thread.start();
- }
- //等待进程结束
- for(auto& thread:threads)
- {
- thread.Join();
- cout<<"wait thread done,thread is: "<<thread.name()<<endl;
- }
- return 0;
- }
复制代码
这样就是对线程的简单封装;
通过封装线程,我们可以更好地控制线程的创建、执行和烧毁过程,同时使得代码更加清晰和易于维护。
此外,封装还可以资助我们添加额外的功能,好比线程池的集成、异常处理、线程同步等。
免责声明:如果侵犯了您的权益,请联系站长,我们会及时删除侵权内容,谢谢合作!更多信息从访问主页:qidao123.com:ToB企服之家,中国第一个企服评测及商务社交产业平台。 |