操作系统系列-进程、线程和协程

操作系统系列-进程、线程和协程


摘要:本文将分别介绍操作系统中的进程、线程和协程,重点关注其区别已经各自的适用场景。最后在python语言环境下,通过测试进程、线程和协程在不同任务下的执行效率来验证以上的观点。
关键词:进程、线程、协程、python

基本概念

进程

进程是操作系统分配资源的独立单位,进程创建和切换的开销较大。进程往往是一个独立的应用,不同进程之间资源相互隔离,因此进程适用于计算密集型应用,可以很好地适配多核CPU。以Python为例,由于全局解释器锁的存在,要想利用多核CPU带来的性能提升,最好使用多进程而不是多线程。进程还有一个好处就是稳定性高,一个进程崩溃了不会影响其他进程。著名的Apache服务器最早采用的就是多进程模式。

线程

线程则相对来说更加地轻量级,线程是CPU调度的最小单位,同一个进程的多个线程可以共享进程的资源,但是会有自己的栈空间,每一个线程的栈空间大概是2mb左右。线程适用于io密集型任务,且这些io任务的耗时比较短。选择运行多少线程数可以与任务的类型和cpu核心数相关,如果是计算密集型任务,线程数大致等于cpu核心数,如果是io密集型任务,线程数可以取cpu核心数的两倍加一,在java并发编程实战中给出了一个如下的公式:
N t h r e a d = N c p u ∗ ( 1 + T w a i t T w o r k ) N_{thread}=N_{cpu} * (1+\frac{T_{wait}}{T_{work}}) Nthread=Ncpu(1+TworkTwait)
实际上,在linux系统中,没有对进程和线程进行太细致的区分,都是由一个名为task_struct的结构体来定义,创建新的进程一般使用fork调用,基于写时复制来解决创建子进程内存拷贝的消耗。线程的一个缺点就是稳定性差,由于一个进程的所有线程共享内存,如果一个线程崩溃了,可能整个进程都会崩溃。

协程

协程则是用户级别的轻量级线程,由于是在用户层面实现的,所以其没有用户态内核态之间切换的开销,但是需要在用户态下实现对于协程的管理和调度。协程适用于io密集型任务,尤其是在这些io耗时较长的情况下。
在golang语言里,通过GMP模型实现了协程的管理和调度,P的数量可以对应各个cpu核心,每一个P都有一个运行队列保存G协程,通过绑定系统线程来运行G。
在python语言中,协程通过关键字yield和send来实现,可以在一个线程中实现并发的生产者和消费者,但是协程的目的是为了能够在遇到io阻塞时,切换到另外一个任务执行,这个切换的过程应该由用户态控制,而不是消耗更大的内核态。因此,可以使用第三方库Gevent来实现遇到io阻塞时自动切换任务

实验

相关实验代码如下:

from multiprocessing import Process
import time
import threading
from gevent import monkey;monkey.patch_all()
import gevent

N = 1000_00
M = 20

# cpu_task定义cpu密集型任务
def cpu_task():
    for i in range(N):
        a = 13
        b = 21
        c = a + b


# io_task_short定义io密集型任务,且io操作耗时较短
def io_task_short():
    for i in range(N // 100):
        a = 1
        time.sleep(0.01)
    
    
# io_task_long定义io密集型任务,且io操作耗时较长
def io_task_long():
    for i in range(N // 1000):
        a = 1
        time.sleep(0.1)
        
              
if __name__ == "__main__":
    for task in [cpu_task, io_task_short, io_task_long]:
        t1 = time.time()
        # 多进程
        procs = []
        for i in range(M):
            p = Process(target=task)
            p.start()
            procs.append(p)
        for p in procs:
            p.join()
        t2 = time.time()
        # 多线程
        threads = []
        for i in range(M):
            t = threading.Thread(target=task)
            t.start()
            threads.append(t)
        for t in threads:
            t.join()
        t3 = time.time()
        # 多协程
        gevent.joinall([gevent.spawn(task) for i in range(M)])
        t4 = time.time()
        print(f"在{task.__name__}任务上,多进程运行时间:{t2 - t1}s,多线程运行时间:{t3 - t2}s,多协程运行时间{t4 - t3}s")        

运行截图如下:
在这里插入图片描述
看起来运行效果一般,需要进一步排查分析。

参考

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值