Python进程线程与协程

119 阅读4分钟
关键字:进程;线程;协程

1. 进程

进程是计算机中资源分配的最小单位,创建一个进程必须创建大量资源,进程内资源共享,在进程结束的时候,子进程总是被父进程销毁。进程接受操作系统调度,受CPU控制。

from multiprocessing import Process
import time
import os

def func1():
    print(123, os.getpid())
    time.sleep(1)
    print(456)

def func2():
    print('aaa', os.getpid())
    time.sleep(1)
    print('bbb')

if __name__ == "__main__":
    Process(target=func1).start()
    Process(target=func2).start()

# 输出:(进程间并行协调运行以缩短时间)
123 15580
aaa 7476
bbb
456
from multiprocessing import Process

def func(a, b):
    c = a + b
    print("子进程", os.getpid())
    return c

if __name__ == "__main__":
    p = Process(target=func, args=(1, 2))
    p.start()
    # p.join()   # 阻塞、等待子进程执行完毕!
    print("主进程", os.getpid())

# 输出:
主进程 5348
子进程 16288

# 注:这里开启一个子进程之后,主进程继续执行后续流程,不等子进程,子进程也去执行他的操作
2. 线程

线程是轻量级进程,进程内线程之间可以共用内存等系统资源,但是,同一个进程下多个线程不能被CPU同时执行。相比于进程,线程速度更快,大部分只涉及网络、文件的操作,多线程更快,CUP执行的时间远小于IO操作和网络时间。

import time
from urllib import request
from threading import Thread

url_list = [
    'https://www.baidu.com', 
    'https://www.bing.com', 
    'https://www.sogou.com', 
    'https://www.qq.com', 
    'https://www.taobao.com'
]

# ---------------------------------1. 原本单线程串行
start_t = time.time()
for url in url_list:
    ret = request.urlopen(url)
print(time.time() - start_t)

# 输出:1.323
# ---------------------------------2. 多线程并发

def get_url(url):
    ret = request.urlopen(url)


start_t = time.time()
t_list = []
for url in url_list:
    t = Thread(target=get_url, args=(url,))
    t.start()
    # t.join()
    t_list.append(t)

for t in t_list:
    t.join()
print(time.time() - start_t)

# 输出:0.722

注:gc和多线程同时使用,程序会发生错乱。线程是由操作系统控制的,对IO感知力强。

3.协程

当操作系统的线程在等待IO的时候,会阻塞当前线程,切换到其他线程,这样当前线程等待IO的时候,其他线程会继续执行。但是当线程数量过多的时候,出现两个问题,其一:过多的线程会占用很大内存资源;其二:线程之间频繁切换会浪费很多时间。协程刚好可以解决此类问题。协程并没有增加线程的数量,协程运行在线程之上,在线程的基础上通过分时复用的方式运行多个协程,即在等待IO的过程中实现重复利用线程。然而,当协程调用了一个阻塞IO操作的时候,操作系统并不知道协程的存在,它只知道线程,操作系统会让线程处于阻塞状态,当前协程和它绑定在该线程上的协程都会处于阻塞状态而不能被调用,因此,协程只有和异步结合才能发挥最大的威力,最好基于编程语言原生支持。因此,在大量IO操作的情况下,采用协程替换线程可以达到很好的效果。

协程由程序控制,对IO感知能力弱,对网络感知能力强,其本质是一条线程,单线程上协调不同程序阻塞和运行的程序。多线程可以规避文件的操作时间,协程能规避网络的操作时间。大多数情况下,进程、线程和协程是配合使用的,比如开5个进程,每个进程20条线程,每个线程500条协程。

import gevent
gevent.spawn(func, c1, c2, c3....)
``
内存测试
# 内存测试
import os
import time
import psutil
import numpy as np
from multiprocessing import Process

def main(N):
    PI = 0
    for n in range(int(N)):
        PI += 1/pow(16,n) * (4/(8*n+1) - 2/(8*n+4) - 1/(8*n+5) - 1/(8*n+6))
    print(PI)

def main2(cycle_num, matrix_n):
    global str_list
    str_list = []
    for i in range(cycle_num):
        str_dict = {}
        matrix = np.mat(np.ones([matrix_n, matrix_n]))
        str_ = '_' * 10000
        str_dict[str_] = matrix
        str_list.append(str_dict)
    print("over")
        
        
if __name__ == "__main__":

    kernel_num = os.cpu_count()
    print("虚拟机核心数目为 {} 核".format(kernel_num))
    mem = psutil.virtual_memory()
    zj = round(float(mem.total/1000000000), 1)
    sys = round(float(mem.used/1000000000), 1)
    kx = round(float(mem.free/1000000000), 1)
    print("内存总量为 {}GB, 使用了 {}GB, 还剩 {}GB".format(zj, sys, kx))
    
    start_time = time.time()
    '''单进程'''
    main2(cycle_num=340, matrix_n=2000)
    
    '''多进程'''
    # Num = 100000
    # thread_list = []
    # for i in range(kernel_num):
    #     print("Process-", i)
    #     t = Process(target=main2, args=(Num, ))
    #     t.start()
    #     thread_list.append(t)
    # for t in thread_list:
    #     t.join()
    end_time = time.time()
    
    print("共耗时{}s".format(end_time - start_time))
# 查看本进程
import os
os.getppid()

# 查看父进程
os.getpid()

# 局部变量自动回收内存,全局变量不回首内存
# 内存回收机制
import gc
del xxx
gc.collect()