快速入门和简单理解并发编程中的并发、并行、同步、异步,并且简单实现多进程和多线程
1. 相关概念简单理解
CPU:中央处理单元,是计算机系统的核心部件,主要负责执行程序、处理任务等。
CPU核数:CPU核心数量,主要负责执行任务,单核CPU只能执行一个任务(同一时刻,只能干一件事)
进程:简单来说,就是一个正在运行的程序,相当于一个正在加工的工厂
线程:线程是进程里面的,也就相当于工厂里面的工人
并行:同一时间,同时执行多个应用程序,多个cpu才能实现并行
并发:伪并行,看似是同时执行的,但实则是cpu内部以很快的速度不断切换执行程序
小例子区分并行和并发:
什么都不是:上完厕所,再吃饭
并行:边上厕所边吃饭
并发:上厕所的时候,吃两口饭,再继续上厕所
接下来是同步和异步:
同步:CPU在执行程序的时候,任务的每一个步骤都是按照顺序执行的,执行下一个之前,前面的必须要先执行完
异步:CPU在执行程序的时候,任务的每一个步骤都是按照顺序执行的,但是不管你是否执行完上面的程序,都会马上往下面继续执行
2. 进程
了解基本概念之后,我们来看进程相关的点
每执行的一个任务都算做一个进程,比如打开了qq音乐,打开了永劫无间,这两个都是不同的进程。
但是现在就有一个问题:早期的CPU是单核的,那么在执行程序的时候,也能边打游戏,边听音乐?
这主要是因为在操作系统内部有一种进程调度算法:“时间片轮转法”
简单来说,就是会给每个应用分配一个时间片,然后CPU交替执行多个进程,由于速度非常快,所以根本感觉不到是在交替执行
2.1 进程的实现
2.1.1 普通进程
了解了进程之后,我们来看看Python中是如何实现进程,并且实现多进程的。
import time
from multiprocessing import Process
def func():
print('正在执行任务 ................')
time.sleep(2)
print('任务执行结束')
if __name__ == '__main__':
start = time.time()
p = Process(target=func)
p.start()
print('主线程执行结束', '执行时间:', time.time() - start)
我们这里用到的是multiprocessing包下面的Process模块,主要用于创建进程
target:传入需要执行的任务(函数),不需要打括号
p.start():开始执行这个进程
但是有时候,我们需要传入参数:
import time
from multiprocessing import Process
def func(a , b):
print('正在执行任务 ................')
time.sleep(2)
print('任务执行结束' , a + b)
if __name__ == '__main__':
start = time.time()
p = Process(target=func , args=(1 , 2))
p.start()
print('主线程执行结束', '执行时间:', time.time() - start)
2.1.2 多进程实现异步
import time
from multiprocessing import Process
def func(name):
print('正在执行任务 ................')
time.sleep(2)
print('我正在看', name)
if __name__ == '__main__':
start = time.time()
lst = ['完美世界', '斗破苍穹', '吞噬星空']
for i in lst:
p = Process(target=func, args=(i , ))
p.start()
print('主线程执行结束', '执行时间:', time.time() - start)
2.1.3 等待进程结束
在每次创建一个进程的时候,都是创建一个子进程
而我们发现,每次我们主进程都执行完了,而子进程还在进行
这个时候,如果我们想让子进程执行完,在执行主进程,我们该 如何去做呢?
import time
from multiprocessing import Process
def func(name):
print('正在执行任务 ................')
time.sleep(2)
print('我正在看', name)
if __name__ == '__main__':
start = time.time()
lst = ['完美世界', '斗破苍穹', '吞噬星空']
p_lst = []
for i in lst:
p = Process(target=func, args=(i , ))
p.start()
p_lst.append(p)
for i in p_lst:
p.join()
print('主线程执行结束', '执行时间:', time.time() - start)
这里我们用到了join方法:
join方法是子进程必须要执行的方法,没有执行的时候,会产生阻塞,可以达到主进程等待子进程执行完的效果。
2.1.4 进程数据隔离
当我们想要在子进程中修改主进程的数据时,我们会发现一个现象
import time
from multiprocessing import Process
num = 10
def func():
global num
num = 0
print('正在执行任务 ................')
time.sleep(2)
print('执行结束')
if __name__ == '__main__':
start = time.time()
p = Process(target=func)
p.start()
p.join()
print('num' , num)
print('主线程执行结束', '执行时间:', time.time() - start)
我们会发现,此时的全局变量num并没有被成功修改
这是因为进程之间都是数据隔离的,并不共享,就跟两个工厂之间,互不影响。
2.1.5 守护进程
如果我们想主进程结束的时候,让子进程也结束,那么这个时候,我们该如何做呢?
这时,我们需要守护进程:
import time
from multiprocessing import Process
num = 10
def func():
global num
num = 0
print('正在执行任务 ................')
time.sleep(2)
print('执行结束')
if __name__ == '__main__':
start = time.time()
p = Process(target=func)
p.daemon = True # 必须放在start前面
p.start()
print('num' , num)
print('主线程执行结束', '执行时间:', time.time() - start)
3. 线程
线程是操作系统能够进行运算调度的最小单位,如果说进程是一个工厂,那么线程就是工厂里面的一个工人。
与进程不相同的是:
1. 同一个进程里面多个线程是共享该进程的资源的,比如一个工厂里面的工人都会经过同一个门。
2. 每一个进程中都会有一个线程,这个线程叫做“主线程”
3.1 实现线程
3.1.1 普通线程
创建线程用的是Threading模块:
from threading import Thread
def func(num):
print('num的值是:',num)
if __name__ == '__main__':
t = Thread(target=func,args=(1,))
t.start()
3.1.2 多线程实现异步
import time
from threading import Thread
start = time.time()
def get_requests(url):
print('正在获取数据')
time.sleep(2)
print('获取结束')
urls = ['1','2','3','4','5']
for url in urls:
t = Thread(target=get_requests,args=(url,))
t.start()
print('总耗时:',time.time()-start)
其实大部分思路和进程一样。
3.1.3 等待线程结束
import time
from threading import Thread
start = time.time()
def get_requests(url):
print('正在获取数据')
time.sleep(2)
print('获取结束')
urls = ['1','2','3','4','5']
ts = []
for url in urls:
t = Thread(target=get_requests,args=(url,))
t.start()
ts.append(t)
for t in ts:
t.join()
print('总耗时:',time.time()-start)
3.1.4 线程数据共享
from threading import Thread
import time
def work():
global n
n = 0 #将全局变量修改为了0
if __name__ == '__main__':
n = 1 #全局变量
t = Thread(target=work)
t.start()
print(n) #结果为0
3.1.5 守护线程
from threading import Thread
import time
def work():
time.sleep(1)
print('子线程正在执行!')
if __name__ == '__main__':
t = Thread(target=work)
t.daemon = True # 必须放在start之前
t.start()
print('主线程结束!')
3.2. 线程池
from multiprocessing.dummy import Pool #导入了线程池模块
import time
urls = ['1','2','3m']
def get_reqeust(url):
print('正在请求数据:',url)
time.sleep(2)
print('请求结束:',url)
start = time.time()
#创建一个线程池,开启了5个线程
pool = Pool(5)
#可以利用线程池中三个线程不断的去处理任务
pool.map(get_reqeust,urls)
print('总耗时:',time.time()-start)
pool.close() #释放线程池
4. 总结
本篇文章快速入门了关于异步编程的一些基本概念和实现,但是还有很多高级内容本文并未提及:锁机制、协程等