在上一篇中,我们已经通过知道如何创建多个线程,本篇会谈论到线程的生命周期,在此之前应该了解一下线程在一个进程中的内存布局
主线程和线程栈
每个栈都是一个独立的虚拟内存分配,可以将其放置在任意位置。重要的是要注意,栈的大小通常是有限的。操作系统保留一定的最大的尺寸例如1MB或8MB)。栈不能超过该大小。但是当固定空间用完时,将触发栈溢出。在实践中这不是问题。实际上,超过合理的栈尺寸被认为是一个错误。而堆由多个段组成。只需添加更多细分即可任意增长。堆由用户模式的C库管理。内核对此一无所知。内核所做的只是在任意的位置提供虚拟内存。
什么是虚拟内存(Virtual Memory),这是涉及到系统内核的内存管理方式足以用一个系列的文章去讲解,以后有机会再说,或者你可以参考相关的文章。
线程栈只是进程所占据的虚拟内存中的一个连续块。 它的最大大小是固定的。 可能看起来像下面的图:
![](https://img.haomeiwen.com/i16148197/bbd5b44142f50256.png)
我们知道,任何C/C++程序运行时,首先运行main函数,在多线程编程中,这个main函数也被称作初始线程或主线程.
- 1)主线程可以通过pthread_create系统调用创建子线程。
- 2)主线程和其他子线程获取在整个CPU时钟周期中获得资源调度是均等的,也就是说所有线程默认状态下是异步独立运行的。
- 3)主线程的确定了所有子线程的生命周期,一但主线程返回或者终止,其他子线程也会终止。
- 4)主线程接受参数得的方式是通过argc和argv,而普通的线程只有一个参数void*类型的指针
- 5)一般来说,主线程默认在栈中可以达到足够的长度,而普通的线程栈的是受到限制的。
- 6)主线程伴随着进程的创建而创建.
线程的生命周期
下面是一个示例,我们在主线程函数暂停5秒,在创建的子线程函数中,我们暂停2秒,目地是检验上面线程的特征的第2点和第3点。
#include <stdio.h>
#include <sys/types.h>
#include <unistd.h>
#include <stdlib.h>
#include <pthread.h>
#include<string.h>
typedef struct{
int age;
char name[15];
} Person;
void *say_hello(void* per){
sleep(2);
Person* p=(Person*)per;
printf("Hi!我的名字叫%s,今年%d岁\n",p->name,p->age);
return (void*)0;
}
int main(int argc,char* argv){
pthread_t tid;
int err;
Person s;
memcpy(s.name,"Peter",15);
s.age=25;
err=pthread_create(&tid,NULL,say_hello,(void*)(&s));
if(err!=0){
printf("创建线程失败\n");
return 0;
}
printf("主线程运行中!!\n");
sleep(5);
return 0;
}
Ok,我们的主线程先于子线程打印字符串,而字符串在调用pthread_create就已经运行,但它被延迟2秒才输出。这样就证明所有线程是独立运行的。
![](https://img.haomeiwen.com/i16148197/9ae2c7f0bff28db1.gif)
如果你我们修改一下上面的实例代码,将main函数中的sleep(5)注释,我们能够看到,子线程没有完全执行之前,主线程已经退出了,从而也强迫子线程跟随终止。
但有时我们的确没必要让主线程等待子线程执行完成后才退出的。使用pthread_exit系统调用可以做到主线程自己执行完后自己退出了(注意:进程没有退出),而子线程仍然可以独立运行直到其退出。
int main(int argc,char* argv){
pthread_t tid;
int err;
Person s;
memcpy(s.name,"Peter",15);
s.age=25;
err=pthread_create(&tid,NULL,say_hello,(void*)(&s));
if(err!=0){
printf("创建线程失败\n");
return 0;
}
printf("主线程运行中!!\n");
int *retval;
pthread_exit(retval);
线程状态
我们会继续探讨一下线程在运行时的四种状态。
- 准备等待可用的CPU资源,其他条件一切准备好。当线程被pthread_create创建时或者阻塞状态结束后就处于准备状态。
- 运行 :线程已经获得CPU的使用权,并且正在运行,在多核心的机器中同时存在多个线程正在运行。如果这种情况不加以控制,会造成整个程序没响应。
-
阻塞:指一个线程在执行过程中暂停,以等待某个条件的触发。
- 例如线程可能在处理有关I/O的任务,可能I/O设备繁忙尚未响应或没有可用的I/O缓存。
- 也可能当前线程等待一个可用的条件便来变量。
- 错误地对一个已被锁住的互斥量加锁
- 调用sigwait等待尚未发生的信号。
-
终止:线程已经从回调函数中返回,或者调用pthread_exit返回,或者被强制终止。
线程运行时的状态切换
小结
我们本篇讨论了主线程和子线程的关系,下一篇我们会讨论线程在运行时的状态。
网友评论