理解 Python 虚拟机:进程、线程和协程

在本篇文章当中深入分析在 Python 当中 进程、线程和协程的区别,这三个概念会让人非常迷惑。如果没有深入了解这三者的实现原理,只是看一些文字说明,也很难理解。在本篇文章当中我们将通过分析部分源代码来详细分析一下这三者根本的区别是什么,重点是协程的应用场景和在 Python 当中是如何使用协程的,至于协程的实现原理在前面的文章当中已经详细讨论过了 深入理解 Python 虚拟机:协程初探——不过是生成器而已 和 深入理解 Python 虚拟机:生成器停止背后的魔法。

进程和线程:

进程是一个非常古老的概念,根据 wiki 的描述,进程是一个正在执行的计算机程序,这里说的计算机程序是指的是能够直接被操作系统加载执行的程序,比如你通过编译器编译之后的 c/c++ 程序。

举个例子,你在 shell 当中敲出的 ./a.out 在按下回车之后,a.out 就会被执行起来,这个被操作系统执行的程序就是一个进程。在一个进程内部会有很多的资源,比如打开的文件,申请的内存,接收到的信号等等,这些信息都是由内核来维护。关于进程有一个非常重要的概念,就是进程的内存地址空间,一个进程当中主要有代码、数据、堆和执行栈:

这里我们不过多的去分析这一点,现在就需要知道在一个进程当中主要有这 4 个东西,而且在内核当中会有数据结构去保存他们。程序被操作系统加载之后可以被操作系统放到 CPU 上运行。我们可以同时启动多个进程,让操作系统去调度,而且随着体系结构的发展,现在的机器上都是多核机器,同时启动多个进程可以让他们同时执行。

在编程时我们会有一个需求,我们希望并行的去执行程序,而且他们可以修改共有的内存,当一个进程修改之后能够被另外一个进程看到,从这个角度来说他们就需要有同一个地址空间,这样就可以实现这一点了,而且这种方式有一个好处就是节省内存资源,比如只需要保存一份内存的地址空间了。

上面谈到的实现进程的方式实际上被称作轻量级进程,也被叫做线程。具体来说就是可以在一个进程内部启动多个线程,这些线程之前有这相同的内存地址空间,这些线程能够同时被操作系统调度到不同的核心上同时执行。我们现在在 linux 上使用的线程是NPTL (Native POSIX Threads Library),从 glibc2.3.2 开始支持,而且要求 linux 2.6 之后的特性。在前面的内容我们谈到了,在同一个进程内部的线程是可以共享一些进程拥有的数据的,比如:

进程号。
父进程号。
进程组号和会话号。
控制终端。
打开的文件描述符表。
当前工作目录。
虚拟地址空间。

线程也有自己的私有数据,比如:

程序执行栈空间。
寄存器状态。
线程的线程号。

在 linux 当中创建线程和进程的系统调用分别为 clone 和 fork,如果为了创建线程的话我们可以不使用这么低层级的 API,我们可以通过 NPTL 提供的 pthread_create 方法创建线程执行相应的方法。

#include
#include

void* func(void* arg) {
printf(“Hello World\n”);
return NULL;
}

int main() {

pthread_t t; // 定义一个线程
pthread_create(&t, NULL, func, NULL); // 创建线程并且执行函数 func

// wait unit thread t finished
pthread_join(t, NULL); // 主线程等待线程 t 执行完成然后主线程才继续往下执行

printf(“thread t has finished\n”);
return 0;

}

编译上述程序:

clang helloworld.c -o helloworld.out -lpth

你可能感兴趣的:(python,java,jvm)