进程 线程 协程

进程

一个具有一定独立功能的程序在一个数据集合上的一次动态执行过程;
进程有自己的代码段、数据段、堆、栈、程序计数器(PC)和一组通用的寄存器值以及相关的系统资源(如打开的文件),进程有内核态和用户态。
在这里插入图片描述
在这里插入图片描述

特点:

  1. 动态性: 可以动态创建、结束进程。
  2. 并发性:进程可以被独立调度并占用处理机运行。
  3. 独立性:不同进程的工作不互相影响,对其他进程透明。
  4. 制约性:访问共享数据、资源或进程间同步而产生制约。

线程

在这里插入图片描述
有三种线程实现的方法,

  1. 用户线程:在用户空间实现,例如POSIX Pthreads, Mach C-threads, Solaris threads。
  2. 内核线程:在内核中实现,例如Windows, Solaris, Linux。
  3. 轻量级进程(LWP):建立在内核之上并由内核支持的用户线程,它是内核线程的高度抽象,每一个轻量级进程都与一个特定的内核线程关联,由内核负责调度。
  • 进程和线程的关系
    在这里插入图片描述
    在这里插入图片描述
    在这里插入图片描述

用户线程

在用户空间实现,由一组用户级的线程库函数来完成线程的创建、终止、同步和调度。操作系统无法感知。
在这里插入图片描述
在这里插入图片描述
在这里插入图片描述

内核线程

通过系统调用由内核实现的线程机制,由内核完成线程的创建、终止和管理。
在这里插入图片描述

  • 内核维护PCB和TCB。
  • 内核线程执行系统调用而被阻塞,不影响其他线程。因为会触发系统调度。
  • 内核线程的创建、终止和管理相对用户线程开销会更大。
  • 调度的单位变成线程,多线程的进程可以获得更多的CPU时间。

轻量级进程 LWP(Light Weight Process)

建立在内核之上并由内核支持的用户线程,它是内核线程的高度抽象,每一个轻量级进程都与一个特定的内核线程关联(可以是一对一,多对一),由内核负责调度。

在这里插入图片描述在计算机操作系统中,轻量级进程(英语:Light-weight process,LWP)是一种实现多任务的方法。与普通进程相比,LWP与其他进程共享所有(或大部分)它的逻辑地址空间和系统资源;与线程相比,LWP有它自己的进程标识符,并和其他进程有着父子关系;这是和类Unix操作系统的系统调用vfork()生成的进程一样的。LWP只能由内核管理并像普通进程一样被调度。Linux内核是支持LWP的典型例子。在大多数系统中,LWP与普通进程的区别也在于它只有一个最小的执行上下文和调度程序所需的统计信息,而这也是它之所以被称为轻量级的原因。

一般来说,一个进程代表程序的一个实例,而LWP代表程序的执行线程(其实,在内核不支持线程的时候,LWP可以很方便地提供线程的实现)。因为一个执行线程不像进程那样需要那么多状态信息,所以LWP也不带有这样的信息。

用户线程和内核线程的对应关系

在这里插入图片描述

示例

系统环境:
Distributor ID: Ubuntu
Description: Ubuntu 16.04.6 LTS
Release: 16.04
Codename: xenial
gcc version 9.3.0 (Ubuntu 9.3.0-23ubuntu1~16.04)

#include <iostream>
#include <thread>
#include <unistd.h>

using namespace std;

int main()
{
    bool loop = true;
    cout << "main thread id " << std::this_thread::get_id() << endl;
    cout << "*************************test start" << endl;

    std::thread th1([&](){
        while(loop)
        {
            sleep(1);
            cout << "thread id " << std::this_thread::get_id() << endl;
        }
    });

    std::thread th2([&](){
        while(loop)
        {
            sleep(1);
            cout << "thread id " << std::this_thread::get_id() << endl;
        }
    });

    std::thread th3([&](){
        while(loop)
        {
            sleep(1);
            cout << "thread id " << std::this_thread::get_id() << endl;
        }
    });

    sleep(2);
    loop = false;
    th1.join();
    th2.join();
    th3.join();
    cout << "*************************test end" << endl;
    return 0;
}

使用GDB调试运行:
在这里插入图片描述
通过pstree工具可以查看到一个进程的进程树(父进程-子进程)
在这里插入图片描述
可以看出该系统版本的线程实现是采用轻量级进程,且用户线程和内核线程是一对一的关系。

协程

协程,英文Coroutines,是一种基于线程之上,但又比线程更加轻量级的存在,这种由程序员自己写程序来管理的轻量级线程叫做『用户空间线程』,具有对内核来说不可见的特性
在这里插入图片描述

协程的特点

  1. 线程的切换由操作系统负责调度,协程由用户自己进行调度,因此减少了上下文切换,提高了效率。
  2. 线程的默认Stack大小是1M,而协程更轻量,接近1K。因此可以在相同的内存中开启更多的协程。
  3. 由于在同一个线程上,因此可以避免竞争关系而使用锁。
  4. 适用于被阻塞的,且需要大量并发的场景。但不适用于大量计算的多线程,遇到此种情况,更好实用线程去解决。

协程的原理

当出现IO阻塞的时候,由协程的调度器进行调度,通过将数据流立刻yield掉(主动让出),并且记录当前栈上的数据,阻塞完后立刻再通过线程恢复栈,并把阻塞的结果放到这个线程上去跑,这样看上去好像跟写同步代码没有任何差别,这整个流程可以称为coroutine,而跑在由coroutine负责调度的线程称为Fiber。比如Golang里的 go关键字其实就是负责开启一个Fiber,让func逻辑跑在上面。

由于协程的暂停完全由程序控制,发生在用户态上;而线程的阻塞状态是由操作系统内核来进行切换,发生在内核态上。
因此,协程的开销远远小于线程的开销,也就没有了ContextSwitch上的开销。

参考:
线程的3种实现方式–内核级线程, 用户级线程和混合型线程
内核线程、轻量级进程、用户线程三种线程概念解惑(线程≠轻量级进程)
一文读懂什么是进程、线程、协程
线程
用户级线程和内核级线程,你分得清吗?
用户态和内核态:用户态线程和内核态线程有什么区别?
清华操作系统MOC

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值