中国太空网站做网站还是网页设计
news/
2025/9/26 1:54:39/
文章来源:
中国太空网站,做网站还是网页设计,苏州网站排名推广,郑州百姓网免费发布信息1.理解用户级线程
我们前面用到的所有跟线程有关的接口全部都不是系统直接提供的接口#xff0c;而是原生线程库pthread提供的接口。我们前面谈到了由于用户只认线程#xff0c;而linux操作系统是通过用轻量级进程模拟线程#xff0c;并不是真正的线程#xff0c;所以linu…1.理解用户级线程
我们前面用到的所有跟线程有关的接口全部都不是系统直接提供的接口而是原生线程库pthread提供的接口。我们前面谈到了由于用户只认线程而linux操作系统是通过用轻量级进程模拟线程并不是真正的线程所以linux的解决方案是中间添加一层软件层向上提供用户所认为的线程接口向下则调用对应的Linux提供的轻量级进程的接口与用户调用的线程接口与之对应所以当我们有五个线程时我们都可以看到有5个不同的LWP。Linux天然的就知道进程管理的那一套方法知道怎么创建如何被调度进程状态是运行还是等待知道怎么回收怎么终止等等这一系列对进程的管理。那么它又是怎么知道创建了几个线程而线程的状态是什么呢创建了几个线程其实我们的用户就知道所以我们把这种线程叫做用户级线程因为它是在用户层被实现的并非在linux操作系统内linux操作系统只有轻量级进程。本身那些用户级线程就要与linux操作系统下的轻量级进程一一对应轻量级进程本身也很多被linux操作系统管理起来了那么线程也相应的需要被管理起来由谁管理那么如何管理呢线程是被pthread库管理起来的所以要先描述在组织。所以就可以通过一个
struct tcb{ //lwp id
} 这样的结构体来进行维护的然后里面的属性应该直接或间接的与lwp id产生相对应的关系pthread库会把linux操作系统提供的跟轻量级进程相关的系统调用接口给封装起来tcb中有些属性这样就可以做到与linux系统产生11的对应关系。如果用户层存在很多很多的线程那么就需要某种数据结构将这些struct tcb对象一个一个的组织起来所以在库里面我们就可以把线程管理起来。 2.系统调用问题
线程要有独立属性
a.上下文
线程要有自己独立的上下文上下文是以轻量级进程的形式维护在PCB当中的。
b.栈
栈在进程地址空间当中其实只有一个而线程要有自己独立的栈但是一般都会有多个线程的那么这个栈该给哪个线程用呢 其实栈只有一个是因为维护栈结构的寄存器只有一套有edpesp所以每一个线程都有自己独立的栈我们该如何理解呢
我们知道linux系统中只有轻量级进程的概念创建轻量级进程的接口是clone,我们下面来认识一下这个接口 其实这个库当中会在堆空间上申请一段空间充当我们的栈然后把地址传入到clone接口里面所以每个新线程的栈在库中维护。其实我们之前谈到的文件有对应的struct file,还有与之对应的文件缓冲区这个缓冲区是在c语言库中维护的所以库自己是可以对一段内存空间做维护同样的道理pthread库也可以维护一段内存空间所以说新线程的栈是在库中维护的而我们的库一般是被放在到进程地址空间中的堆栈之间的共享区的动态库是加载到这里动态库一旦被加载到物理内存当中然后通过页表映射到共享区当中新线程创建的时候pthread库会在堆中new出来一段空间然后用指针进行维护给新创建的线程通过clone接口把该地址作为栈的起始地址传进去这就给线程维护了独立的栈那么当线程终止的时候只需要把这段空间释放掉就可以了可是在有新线程之前一定是要先有主线程的默认地址空间当中的栈是给主线程使用的。 3.理解phtread库管理线程 假设这是我们自己的多线程程序mythread,当我们的mythread允许时会将数据代码加载到内存当中如果是单纯的打印不用库的程序直接就可以运行了但是今天我们这个程序使用到了pthread动态库的要想使用一系列的pthread_create创建线程pthread_join线程等待访问pthread_t等这些方法之类的需要先看到我们对应的库所以动态库也是要被加载到内存中的然后通过页表映射到进程地址空间的共享区当中这个库就可以提供各种线程相关的方法属性操作系统就可以创建相应的PCB了。当然这还只是单进程考虑到操作系统中有多个进程多个进程也会创建对应的多线程然后都会用到对应的pthread库pthread是动态库可以共享的所以该操作系统内的所有线程都要依赖于这个库用这同一个pthread库,只需加载到内存中一次就可以一直被使用所有线程被这个pthread库所管理所以我们称这个库是共享库。也就是说线程库是共享的所以内部要管理整个系统多个用户启动的所有线程。 假设主线程是上图中tid2要通过pthread_joinpthread_t , ret获取tid的退出结果。这里的pthrea_t tid是线程属性集合在库中的地址。 4.站在语言角度理解pthread
因为我们知道c11已经开始支持多线程了下面我们用一段c语言的代码来写一段多线程代码
mythread.cc
#includeiostream
#includeunistd.h
#includethread
#includecstdlibusing namespace std;void myrun()
{while(true){coutI am a thread!endl;sleep(1);}
}int main()
{thread t(myrun);t.join();return 0;
} makefile:
mythread:mythread.ccg -o $ $^ -stdc11 .PHONY:clean
clean:rm -rf mythread
编译运行之后 我们发现运行不了然后我们编译时加上-lpthread
makefile:
mythread:mythread.ccg -o $ $^ -stdc11 -lpthread.PHONY:clean
clean:rm -rf mythread 然后再编译一次再运行 发现没有报错了。
我们再打开监视窗口 发现确实是两个线程。
这说明了什么呢如果不加上-lpthread那么这段代码就运行不了说明c11支持的多线程底层封装了pthread库也是用到原生线程库pthread的。
5.理解线程局部存储
我们直接先用一段代码来进行现象说明
#include iostream
#include string
#include cstdlib
#include unistd.h
#include sys/types.h
#include sys/syscall.h
#include pthread.husing namespace std;int g_val 100; // 全局变量本身就是被所有线程共享的void *threadRoutine(void *args)
{std::string name static_castconst char *(args);while (true){sleep(1);std::cout name , g_val: g_val ,g_val: g_val \n std::endl;g_val;}return nullptr;
}int main()
{pthread_t tid;pthread_create(tid, nullptr, threadRoutine, (void *)thread1);while (true){sleep(1);std::cout main thread, g_val: g_val ,g_val: g_val \n std::endl;}pthread_join(tid, nullptr);return 0;
}
运行结果 我们发现g_val的值一旦被新线程改变主线程立马就能看到全局变量本身就是被所有线程共享的。
如果我们在g_val前面加上一个__thread
__thread int g_val 100; // 全局变量本身就是被所有线程共享的
其他代码不变的情况下的运行结果 我们发现主线程的g_val与新线程的g_val的值和地址都不一样这就是用到了线程的局部存储。
而获取线程的lwp我们可以通过这个接口获取syscall(SYS_gettid);
下面用一段代码来进行获取 #include iostream
#include string
#include cstdlib
#include unistd.h
#include sys/types.h
#include sys/syscall.h
#include pthread.husing namespace std;// int g_val 100; // 全局变量本身就是被所有线程共享的
__thread int g_val 100; // 线程的局部存储有什么用有什么坑__thread pid_t lwp 0;// __thread std::string threadname;pid_t gettid() {return syscall(SYS_gettid);
}void *threadRoutine(void *args)
{std::string name static_castconst char *(args);lwp gettid(); // 调用系统调用 SYS_gettid 获取当前线程的 TIDwhile (true){sleep(1);std::cout name , g_val: g_val ,g_val: g_val \n std::endl;std::cout new thread: lwp std::endl;g_val;}return nullptr;
}int main()
{pthread_t tid;pthread_create(tid, nullptr, threadRoutine, (void *)thread1);lwp gettid(); // 调用系统调用 SYS_gettid 获取当前线程的 TIDstd::cout main thread: lwp std::endl;while (true){sleep(1);std::cout main thread, g_val: g_val ,g_val: g_val \n std::endl;}pthread_join(tid, nullptr);
}
运行结果 这就获取到了线程的lwp. 6.用c代码进行线程封装
//thread.hpp
#pragma once
#includeiostream
#includestring
#includefunctional
#includepthread.h
templateclass T
using func_t std::functionvoid(T);templateclass T
class Thread
{
public:Thread(const std::string threadname,func_tT func,T data):_tid(0),_isrunning(false),_threadname(threadname),_func(func),_data(data){}static void* ThreadRoutine(void * args){Thread* ts static_castThread*(args);ts-_func(ts-_data);return nullptr;}bool Start(){int n pthread_create(_tid,nullptr,ThreadRoutine,this);if(n0){_isrunning true;return true;}return false;}bool Join(){if(!_isrunning) return true;int n pthread_join(_tid,nullptr);if(n0){_isrunning false;return true;}return false;} std::string ThreadName(){return _threadname;}bool IsRunning(){return _isrunning;}~Thread(){}private:pthread_t _tid;bool _isrunning;std::string _threadname;func_tT _func;T _data;
}; //main.cc
#includeiostream
#includestring
#includeunistd.h
#includeThread.hppstd::string GetThreadName()
{static int num 1;char name[64];snprintf(name,sizeof(name),Thread-%d,num);return name;
}void Print(int num)
{while(num){std::couthello world: num--std::endl;sleep(1);}
}int main()
{Threadint t(GetThreadName(),Print,10);std::coutthread is running? t.IsRunning()std::endl;t.Start();std::coutthread is running? t.IsRunning()std::endl;t.Join();return 0;
}
Makefile
thread_test:main.ccg -o $ $^ -stdc11 -lpthread.PHONY:clean
clean:rm -rf thread_test 运行结果:
本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.mzph.cn/news/917775.shtml
如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!