进程、线程、协程

在这里插入图片描述

前言

例如:随着人工智能的不断发展,机器学习这门技术也越来越重要,很多人都开启了学习机器学习,本文就介绍了机器学习的基础内容。

首先这三者是有明显的大小关系
进程 > 线程 > 协程
一个进程可以有多个线程,一个线程里面可以有多个协程


一、易混概念

1.1 同步vs异步

同步(sync):发出一个功能调用时,在没有得到结果之前,该调用就不返回或继续执行后续操作。 简
单来说,同步就是必须一件一件事做,等前一件做完了才能做下一件事。
异步( async):当一个异步过程调用发出后,调用者在没有得到结果之前,就可以继续执行后续操
作。
在这里插入图片描述
同步和异步的区别:请求发出后,是否需要等待结果,才能继续执行其他操作。

1.2 并发vs并行

并发(Concurrency):指在行,但多个进程指令被快速的轮换执行,使得在宏观上具有多个进程同时执行的效果,但在微观上并不是同时执行的,只是把时间分成若干段,使多个进程快速交替的执行。
在这里插入图片描述

并行(Parallelism):有多条指令在多个处理器上同时执行。所以无论从微观还是从宏观来看,二者都是一起执行的。
在这里插入图片描述

二、进程(Process)

2.1进程概念

一台计算机所能利用的资源总是有限的,比如 CPU 在 1 秒钟之内最多执行 1 亿条指令,计算机一共有6GB 的内存空间等等。因此,“如何提高计算机资源的利用率”是人们一直思考的问题,这个问题也一直带动着计算机硬件和软件的发展。

计算机发展早期,程序的控制历经:管理员>批处理>多任务操作系统

为了使多任务操作系统更高效地完成计算机资源的分配和回收,便于管理各个程序的执行过程,人们提出了“进程”的概念。

  • 进程是一个实体,结构包含程序、数据和进程控制块。
  • 每一个进程都有它自己的地址空间,一般情况下,包括文本区域(text region)、数据区域(data region)和堆栈(stack region)。
    • 文本区域存储处理器执行的代码;
    • 数据区域存储变量和进程执行期间使用的动态分配的内存;
    • 堆栈区域存储着活动过程调用的指令和本地变量
  • 进程是一个“执行中的程序”。程序是一个没有生命的实体,只有处理器赋予程序生命时(操作系统执行之),它才能成为一个活动的实体,我们称其为进程。

2.2 进程三个基本状态

进程执行时的间断性,决定了进程可能具有多种状态。事实上,运行中的进程可能具有以下三种基本状态。

1)就绪状态(Ready):
    进程已获得除处理器外的所需资源,等待分配处理器资源;只要分配了处理器进程就可执行。就绪进程可以按多个优先级来划分队列。例如,当一个进程由于时间片用完而进入就绪状态时,排入低优先级队列;当进程由I/O操作完成而进入就绪状态时,排入高优先级队列。

2)运行状态(Running):执行状态
    进程占用处理器资源;处于此状态的进程的数目小于等于处理器的数目。在没有其他进程可以执行时(如所有进程都在阻塞状态),通常会自动执行系统的空闲进程。

3)阻塞状态(Blocked):
    由于进程等待某种条件(如I/O操作或进程同步),在条件满足之前无法继续执行。该事件发生前即使把处理器资源分配给该进程,也无法运行。
在这里插入图片描述
(1) 就绪→运行态
处于就绪状态的进程,当进程调度程序为之分配了CPU后,该进程便由就绪状态转变成执行状态。

(2) 运行→就绪
处于执行状态的进程在其执行过程中,因分配给它的一个时间片已用完而不得不让出CPU,于是进
程从执行状态转变成就绪状态。

(3) 运行→阻塞
正在执行的进程因等待某种事件发生而无法继续执行时,便从执行状态变成阻塞状态。

(4) 阻塞→就绪
处于阻塞状态的进程,若其等待的事件已经发生,于是进程由阻塞状态转变为就绪状态。

2.3多进程方式编程

from multiprocessing import Process
import os
import time


# 子进程要执行的代码
def task_process(delay):
    num = 0
    for i in range(delay*10000000):
        num+=i
    print(f"进程pid为 {os.getpid()},执行完成")



if __name__=='__main__':
    print( '父进程pid为 %s.' % os.getpid())
    t0 = time.time()
    task_process(3)
    task_process(3)
    t1 = time.time()
    print(f"顺序同步方式执行耗时 {t1-t0} ")

    p0 = Process(target=task_process, args=(3,))
    p1 = Process(target=task_process, args=(3,))
    t2 = time.time()
    p0.start();p1.start()
    p0.join();p1.join()
    t3 = time.time()
    print(f"多进程并发执行耗时 {t3-t2}")

在这里插入图片描述

三、线程(Thread)

3.1 线程的引入

    早期的多任务操作系统,以进程为单位管理各个程序的运行以及计算机资源的分配和回收,进一步提高了计算机资源的利用率。但随着计算机硬、软件的发展,人们发现还可以做进一步优化,例如:

  • 操作系统将 CPU 资源从一个进程分配给另一个进程时,开销较大;
  • 各个进程占用的内存空间是相互独立的,大大增加了进程间通信的实现难度;
  • 一个进程可能会执行多个任务,当某个任务因 I/O 操作暂停执行时,其他任务将无法执行。

在计算机软、硬件快速发展,人们计算机运行效率的要求越来越高的大背景下,“线程”应运而生。

3.2 线程概念

    线程是操作系统能够进行运算调度的最小单位,它被包含在进程之中,是进程中的实际运作单位。一条线程指的是进程中一个单一顺序的控制流,一个进程中可以并发多个线程,每条线程并行执行不同的任务,比如一段程序、一个函数等。

    线程和进程之间的关系,类似于工厂和工人之间的关系,进程好比是工厂,线程就如同工厂中的工人。一个工厂可以容纳多个工人,工厂负责为所有工人提供必要的资源(电力、产品原料、食堂、厕所等),所有工人共享这些资源,每个工人负责完成一项具体的任务,他们相互配合,共同保证整个工厂的平稳运行。

每个进程执行前,操作系统都会为其分配所需的资源,包括要执行的程序代码、数据、内存空间、文件资源等。一个进程至少包含 1 个线程,可以包含多个线程,所有线程共享进程的资源,各个线程也可以拥有属于自己的私有资源。
在这里插入图片描述

3.3 多线程编程

多线程概念:

一个进程中拥有多(≥2)个线程,线程之间相互协作、共同执行一个应用程序。

通常将以“多线程”方式编写的程序称为“多线程程序”,将编写多线程程序的过程称为“多线程编程”,将拥有多个线程的进程称为“多线程进程”。

当进程中仅包含 1 个执行程序指令的线程时,该线程又称“主线程”,这样的进程称为“单线程进
程”。

很多应用程序(软件)都是多线程程序,例如 QQ 具备同时和多人聊天的能力、迅雷具备同时下载多个资源的能力、很多杀毒软件可以同时开启杀毒、清理垃圾、电脑加速等功能。

3.4 GIL对多线程的影响

import threading, time

def my_counter():
    total = 100_000_000
    i = 0
    for _ in range(total):
        i = i+1
        if i % (total/10) == 0:
            print(f"当前进度:{i/total:.2%} ")
    return True

def main1():
    thread_ary = {}
    start = time.time()
    for tid in range(2):
        t = threading.Thread(target=my_counter)
        t.start()
        t.join()  # 第1次循环,join方法引起主线程阻塞,但第2个线程并没有启动,所以2个线程是顺序执行的
    end = time.time()
    print(f"单线程顺序执行方式:total_time: {end - start:.2f}")

def main2():
    thread_ary = {}
    start = time.time()
    for tid in range(2):
        t = threading.Thread(target=my_counter)
        t.start()
        thread_ary[tid] = t

    for i in range(2):
        thread_ary[i].join() # 两个线程均已启动,所以两个线程是并发的
    end = time.time()

    print(f"多线程并发执行方式:total_time: {end-start:.2f}")

if __name__ == "__main__":
    main1()
    main2()
100_000_000
单线程顺序执行方式:total_time: 36.82
多线程并发执行方式:total_time: 36.97
1_000_000_000
单线程顺序执行方式:total_time: 368.88
多线程并发执行方式:total_time: 366.76

3.5 GIL是否意味着线程安全

并不是。GIL是用来保护Python解释器的线程安全,不是用来保护用户线程的线程安全,软件开发人员需要自己解决。

解释器本身有大量的变量,是需要进行保护的。假设没有GIL时,两个线程都需要抛出异常,那么异常应该如何存储和处理,改变解释器的状态,就成了问题。GIL在此环境里就限制了解释器本身只有一个线程处于运行中,任意Python解释器级别操作都是串行的,使得任一时间都只能有最多一个语句抛出异常。于是异常相关的共享变量就得到了保护。但对于Python程序里,用户创建的线程,就需要程序员自己利用threading里的设施来进行并发保护。因为一个Python语句,往往对应了底层的多个Python解释器操作,这些操作的执行顺序可能会被操作系统的抢占式多任务打乱的。所以不得不进行保护。

举个例子,打印语句:
print("hello world")
其底层就至少可以分为3个操作,现实中可能操作更多:
sys.sdout.write("hello world")
sys.stdout.write("\n")
sys.stdout.flush()

所以在多个线程一起执行 print(“hello world”) 时,屏幕上就可能看到同一行里多个hello world,这就是语句内部的操作被抢占式多任务打乱的结果。

四、协程(Coroutine)

4.1 协程概念

是一个更小的概念,它是位于线程内部,完全受程序所控制,开销小,协程,又称微线程,也称为用户级线程,在不开辟线程的基础上完成多任务,也就是在单线程的情况下完成多任务,多个任务按照一定顺序交替执行,它的特点就是运行效率极高。


总结

提示:这里对文章进行总结:

例如:以上就是今天要讲的内容,本文仅仅简单介绍了pandas的使用,而pandas提供了大量能使我们快速便捷地处理数据的函数和方法。
在这里插入图片描述

评论 4
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

么凹猫'

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值