传媒公司网站建设方案,如何做网站卡密,微擎 wordpress,深圳网络建设有限公司本文重点#xff1a;理解线程控制的接口 
前言 
内核中是没有很明确线程的概念的#xff0c;只有轻量级进程的概念#xff0c;不会提供直接给我们线程的系统调用#xff0c;而会给我们提供轻量级进程的系统调用。我们用户是需要线程的接口的#xff0c;在应用层#xff0…本文重点理解线程控制的接口 
前言 
内核中是没有很明确线程的概念的只有轻量级进程的概念不会提供直接给我们线程的系统调用而会给我们提供轻量级进程的系统调用。我们用户是需要线程的接口的在应用层人们封装了轻量级进程的系统调用为用户直接提供线程的接口这个封装的就是线程库pthread库这个是第三方库几乎所有的Linux系统都会自带这个库当我们进行编译链接时要指定这个库 
线程的创建 
函数pthread_create 参数: thread:输出型参数返回线程ID attr:设置线程的属性attr为nullptr表示使用默认属性 start_routine:是个函数指针线程启动后要执行的函数 arg:传给线程启动函数的参数线程被创建成功新线程回调线程函数的时候需要参数这个参数是给线程函数传递的 返回值成功返回0失败返回错误码 传统的一些函数是成功返回0失败返回-1并且对全局变量errno赋值以指示错误。 pthreads函数出错时不会设置全局变量errno而大部分其他线程函数会这样做。而是将错误代码通过返回值返回 pthreads同样也提供了线程内的errno变量以支持其它使用errno的代码。对于pthreads函数的错误建议通过返回值判定因为读取返回值要比读取线程内的errno变量的开销更小 
线程的函数的参数和返回值不仅仅可以用来进行传递一般参数也可以传递对象 
#includeiostream
#includepthread.h
#includeunistd.h
#includestdlib.husing namespace std;void* threadroutine(void * arg)
{const char* name(const char*)arg;while(true){coutname,pid: getpid()endl;sleep(2);}
}int main()
{pthread_t tid;pthread_create(tid,nullptr,threadroutine,(void*)new thread);while(true){coutmain thread,pid: getpid()endl;sleep(1);}return 0;
} 发现它们的pid是一样的 ps  -aL   查看系统轻量级进程  LWPlight weight process:线程ID这个线程ID属于进程调度的范畴。因为线程是轻量级进程是操作系统调度器的最小单位所以需要一个数值来唯一表示该线程。 
我们发现一个PID和LWP是一样的那这个就是主线程其他就是创建出来的线程  
线程组内的第一个线程在用户态被称为主线程(main thread),内核在创建第一个线程时会将线程组的ID的值设置成第一个线程的线程ID既主线程的进程描述符。所以线程组内存在一个线程ID等于进程ID而该线程即为线程组的主线程 
至于线程组其他线程的ID则有内核负责分配其线程组ID总是和主线程的线程组ID一致无论是主线程直接创建线程还是创建出来的线程再次创建线程都是这样 
强调一点线程和进程不一样进程有父进程的概念但在线程组里面所有的线程都是对等关系 
线程的终止 
如果需要只终止某个线程而不终止整个进程,可以有三种方法: 1. 从线程函数return。这种方法对主线程不适用,从main函数return相当于调用exit。 2. 线程可以调用pthread_ exit终止自己。 3. 一个线程可以调用pthread_ cancel终止同一进程中的另一个线程。 
注意以前的进程终止函数exit是用来终止进程的 
函数pthread_exit 参数和线程函数的返回值类型是一样的都是void*这就和return终止线程差不多了 
需要注意,pthread_exit或者return返回的指针所指向的内存单元必须是全局的或者是用malloc分配的,不能在线程函数的栈上分配,因为当其它线程得到这个返回指针时线程函数已经退出了。 
函数pthread_cancel 
取消执行中的线程 线程的等待 
在进程里有个进程等待父进程等待子进程是为了防止僵尸进程回收子进程防止内存泄漏 
为什么需要线程等待已经退出的线程其空间没有被释放仍然在进程的地址空间内。创建新的线程不会复用刚才退出线程的地址空间。那么线程中也要有线程等待如果没有那么也会有类似于僵尸的状态 
函数pthread_join 函数的作用等待指定线程可以获取这个线程函数的返回值void*终止状态 参数 thread:线程ID value_ptr:它指向一个指针就是指向线程创建时调用的线程函数的返回值void*返回值成功返回0失败返回错误码 调用该函数的线程将阻塞等待,直到id为thread的线程终止。thread线程以不同的方法终止,通过pthread_join得到的终止状态是不同的总结如下: 1. 如果thread线程通过return返回,value_ ptr所指向的单元里存放的是thread线程函数的返回值。 2. 如果thread线程被别的线程调用pthread_ cancel异常终掉,value_ ptr所指向的单元里存放的是常数PTHREAD_CANCELED一个宏常数就是-1。 3. 如果thread线程是自己调用pthread_exit终止的,value_ptr所指向的单元存放的是传pthread_exit的参数。 4. 如果对thread线程的终止状态不感兴趣,可以传nullptr给value_ ptr参数。  
#includeiostream
#includepthread.h
#includeunistd.h
#includestdlib.husing namespace std;void* threadroutine(void * arg)
{const char* name(const char*)arg;int cnt5;while(true){coutname,pid: getpid()endl;sleep(1);cnt--;if(cnt0) break;}return (void*)1;//我的当前平台地址是64位8字节而int是4字节不进行强转会导致混乱
}int main()
{pthread_t tid;pthread_create(tid,nullptr,threadroutine,(void*)new thread);void * retval;pthread_join(tid,retval);// main thread等待的时候默认是阻塞等待的cout  main thread quit ..., ret:   (long long int)retval  endl;return 0;
} 为什么我们在这里join的时候不考虑异常呢因为做不到线程异常了整个进程都会挂掉。线程的健壮性低 
线程的分离 
上面的线程等待只能阻塞等待那如果不想阻塞怎么办并且不关心线程的返回值那就让线程分离线程分离之后主线程就不管了主线程就干自己的事了自己的资源自己释放回收了虽然线程分离了但是还是属于这个进程的 
可以是线程组内其他线程对目标线程进行分离也可以是线程自己分离 
joinable和分离是冲突的一个线程不能既是joinable又是分离的也就是说线程分离了主线程就不用等待回收线程了 #includeiostream
#includecstring
#includeunistd.h
#includepthread.h
void* PthreadRoutine(void* args)
{int i0;pthread_detach(pthread_self());while(i3){coutchild thread ,pid: getpid()endl;i;sleep(1);}return nullptr;
}
int main()
{pthread_t tid;pthread_create(tid,nullptr,PthreadRoutine,nullptr);sleep(1);//确保分离成功int npthread_join(tid,nullptr);printf(n  %d, who  0x%x, why: %s\n, n, tid, strerror(n));return 0;}线程ID及进程地址空间布局 
pthread_ create函数会产生一个线程ID存放在第一个参数指向的地址中。该线程ID和前面说的线程ID不是一回事。前面讲的线程ID属于进程调度的范畴。因为线程是轻量级进程是操作系统调度器的最小单位所以需要一个数值来唯一表示该线程。 pthread_ create函数第一个参数指向一个虚拟内存单元该内存单元的地址即为新创建线程的线程ID属于NPTL线程库的范畴。线程库的后续操作就是根据该线程ID来操作线程的。 线程库NPTL提供了pthread_ self函数可以获得线程自身的ID 
函数pthread_self 函数clone  这个函数clone是系统调用用来创建轻量级进程的前言说的pthread库的底层就是用这个函数封装的 使用线程库是要加载到内存的它是动态库所以会在地址空间的共享区。 
线程的概念是库给我们维护的线程库注定要维护多个线程线程库要管理这些线程先描述再组织每个线程都会有一个库级别的结构体tcb这个结构体的起始地址就是线程的id  除了主线程外所有的其他线程的独立栈都在共享区具体来讲是在pthread库中tid指向用户的tcb。全局变量和堆区都是线程共享的栈不是每个线程都有自己的独立栈 目前我们的原生线程pthread库原生线程库C11 语言本身也已经支持多线程了 vs 原生线程库。其实C的多线程底层封装的就是Linux系统下的原生线程库为什么C移值性高就是因为C的代码在不同平台上都可以跑在Linux下跑多线程底层就是Linux的原生线程库在Windows跑底层就是Windows的原生线程库C语音在底层设计时不同的系统有不同的设计但是在上层看来都一样 
#include iostream
#include threadusing namespace std;void threadrun()
{while(true){cout  I am a new thead for C  endl;sleep(1);}
}int main()
{thread t1(threadrun);t1.join();return 0;
} 
多线程的创建 
1.创建多线程 
#includeiostream
#includestring
#includevector
#includeunistd.h
#includepthread.husing namespace std;#define NUM 4class ThreadData
{
public:ThreadData(int number){_threadnamethread-to_string(number);}
public:string _threadname;
};string toHex(pthread_t tid)
{char buf[128];snprintf(buf,sizeof(buf),0x%x,tid);return buf;}
void* ThreadRoutine(void* args)
{ThreadData* tdstatic_castThreadData*(args);string tidtoHex(pthread_self());pid_t pidgetpid();int i0;while(i3){couttd-_threadname pid: pid tid: tidendl;i;sleep(1);}delete td;return nullptr;
}
int main()
{vectorpthread_t tids;//创建多线程for(int i1;iNUM;i){pthread_t tid;ThreadData* tdnew ThreadData(i);pthread_create(tid,nullptr,ThreadRoutine,td);tids.push_back(tid);sleep(1);}//等待多线程for(auto tid:tids){pthread_join(tid,nullptr);}coutmain thread wait successendl;return 0;
}上面代码在主线程开辟的堆空间传递给了子线程子线程还可以利用说明堆空间是被所有线程共享的 
2.验证全局变量共享 
在上面代码上加个全局变量g_val在子线程打印他的地址 
#includeiostream
#includestring
#includevector
#includeunistd.h
#includepthread.husing namespace std;#define NUM 4
int g_val0;//定义一个全局class ThreadData
{
public:ThreadData(int number){_threadnamethread-to_string(number);}
public:string _threadname;
};string toHex(pthread_t tid)
{char buf[128];snprintf(buf,sizeof(buf),0x%x,tid);return buf;}
void* ThreadRoutine(void* args)
{ThreadData* tdstatic_castThreadData*(args);string tidtoHex(pthread_self());pid_t pidgetpid();int i0;while(i3){couttd-_threadname pid: pid tid: tid,g_val: g_val,g_val: g_valendl;i;sleep(1);}delete td;return nullptr;
}
int main()
{vectorpthread_t tids;//创建多线程for(int i1;iNUM;i){pthread_t tid;ThreadData* tdnew ThreadData(i);pthread_create(tid,nullptr,ThreadRoutine,td);tids.push_back(tid);sleep(1);}//等待多线程for(auto tid:tids){pthread_join(tid,nullptr);}coutmain thread wait successendl;return 0;
}发现这个全局变量的地址都是一样的说明全局变量被所有线程共享 
3.线程的局部存储 
如果线程不想共享全局变量想要自己私有的全局变量那么在全局变量加__thread修饰两个下划线这个是个编译选项只能用来定义内置类型不能修饰自定义类型那么这个全局变量就不在全局区了就在线程的栈结构的局部存储中这个是线程级别的全局变量也就是在某个线程里面时全局的 地址不一样并且这个地址挺大的说明在中间的堆栈之间 
4.验证线程具有独立栈结构 
在每个线程里面加个变量test_i打印他的地址 
#includeiostream
#includestring
#includevector
#includeunistd.h
#includepthread.husing namespace std;#define NUM 4class ThreadData
{
public:ThreadData(int number){_threadnamethread-to_string(number);}
public:string _threadname;
};string toHex(pthread_t tid)
{char buf[128];snprintf(buf,sizeof(buf),0x%x,tid);return buf;}
void* ThreadRoutine(void* args)
{ThreadData* tdstatic_castThreadData*(args);string tidtoHex(pthread_self());pid_t pidgetpid();int i0;int test_i0;while(i3){couttd-_threadname pid: pid tid: tid,test_i: test_i,test_i: test_iendl;;i;sleep(1);}delete td;return nullptr;
}
int main()
{vectorpthread_t tids;//创建多线程for(int i1;iNUM;i){pthread_t tid;ThreadData* tdnew ThreadData(i);pthread_create(tid,nullptr,ThreadRoutine,td);tids.push_back(tid);sleep(1);}//等待多线程for(auto tid:tids){pthread_join(tid,nullptr);}coutmain thread wait successendl;return 0;
}发现test_i的地址都不一样说明它们有独立的栈结构并不是私有的栈结构主线程想访问也可以访问的线程中没有秘密如果要访问还是可以访问的