并发和并行
并发:单核CPU在不同时刻只执行一个任务,在同一时间段内,交替执行两个任务。
并行:双核CPU可以在同一时刻执行两个任务。
多核CPU的每个核心都可以独立执行一个任务,而且多个核心之间不会相互干扰。
并发+并行:所有核心都要并行工作,并且每个核心还要并发工作。
总之:
单核CPU只能并发,无法并行;
多核CPU,并发和并行一般同时存在,它们都是提高CPU处理任务能力的重要手段。
Python 并发:多线程、多进程的实现
GIL:全局解释器锁(Global Interpreter Lock)是一个互斥锁,它只允许一个线程来控制Python解释器。在任何时间点只有一个线程可以处于执行状态。执行单线程程序的开发人员感受不到GIL的影响,但它可能是CPU限制型和多线程代码中的性能瓶颈。
GIL是解释器本身的单个锁,它增加了一条规则,即执行任何Python字节码都需要获取解释器锁。这可以防止死锁(因为只有一个锁)并且不会引入太多的性能开销。但是即使在具有多个CPU核心的多线程架构中,GIL一次只允许一个线程执行。
Python是运行在解释器中的语言,python中的全局锁(GIL),在使用多进程(Thread)的情况下,不能发挥多核的优势。而使用多进程(Multiprocess),则可以发挥多核的优势真正地提高效率。
处理GIL
(1)使用多进程代替多线程。每个Python进程都有自己的Python解释器和内存空间,因此GIL不会成为问题。Python中的(Multiprocess)模块可以轻松创建进程。
在python多线程下,每个线程的执行方式是:
1、获取GIL
2、执行代码知道sleep或者python虚拟机将其挂起
3、释放GIL
可见,每个线程想要执行,必须拿到GIL,并且在python进程中,GIL只有一个。
python下想要充分利用多核CPU,就用多进程。
原因在于:每个进程有各自独立的GIL,互不干扰,这样就可以实现真正意义上的并行。所以在多核CPU中,多进程执行效率优于多进程(仅针对多核CPU而言)。
(2)更换Python解释器。Python有多个解释器。分别用C、Java、C#和Python写的CPython、Jython、IronPython和PyPy是最受欢迎的。GIL仅存在于CPython的原始Python实现中。(CPython使用最广,怎样判断自己的解释器)
(3)等待。虽然许多Python用户利用了GIL的单线程性能优势。多线程程序员不必烦恼,因为Python社区中一些最聪明的人正在努力从CPython中删除GIL。
Thread中的join方法 : join方法底层其实就是一个wait方法。
jion()阻塞主线程
- jion()作用为阻塞主线程,在子线程未返回的时候,主线程等待其返回然后继续执行
- jion不能与star在循环里连用
以下为错误代码,代码创建了5个线程,然后用一个循环激活线程,激活之后令其阻塞主线程
threads = [Thread() for i in range(5)]
for thread in threads:
thread.start()
thread.join()
执行过程:
1、第一次循环中,主线程通过start函数激活线程1,线程1进行计算
2、由于start函数不阻塞主线程,在线程1进行运算的同时。主线程向下执行jion函数
3、执行jion函数之后,主线程被线程1阻塞,在线程1返回结果之前,主线程无法执行下一轮循环
4、线程1计算完成之后,解除对主线程的阻塞
5、主线程进入下一轮循环,激活线程2并被阻塞
如此往复,可以看出,本来应该并发五个线程,在这里变成顺序队列,效率和单线程无异
jion的正确用法:使用两个循环分别处理start和jion函数,即可实现并发
threads = [Thread() for i in range(5)]
for thread in threads: //A循环
thread.start()
for thread in threads://B循环
thread.join()
执行过程:
1、A循环中,依次开启线程1~线程5,,线程1到线程5并发执行
2、B循环中,线程1到线程5依次对主线程进行阻塞
3、线程1到线程5执行结束,依次解除对主线程的阻塞。
4、线程1到线程5全部执行完毕,主线程开始运行。
单线程:
import requests
import time
from threading import Thread
from multiprocessing import Process
def count(x, y): //定义CPU密集的计算函数
# 使程序完成50万计算
c = 0
while c < 500000:
c += 1
x += x
y += y
def write(): //定义IO密集的文件读写函数
f = open("test.txt", "w")
for x in range(5000000):
f.write("testwrite\n")
f.close()
def read():
f = open("test.txt", "r")
lines = f.readlines()
f.close()
_head = {
'User-Agent': 'Mozilla/5.0 (Windows NT 10.0; WOW64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/48.0.2564.116 Safari/537.36'}
url = "http://www.tieba.com"
def http_request(): //定义网络请求函数
try:
webPage = requests.get(url, headers=_head)
html = webPage.text
return {"context": html}
except Exception as e:
return {"error": e}
测试线性执行IO密集操作、CPU密集操作所需时间、网络请求密集型操作所需时间
# CPU密集操作
t = time.time()
for x in range(10):
count(1, 1)
print("Line cpu", time.time() - t)
# IO密集操作
t = time.time()
for x in range(10):
write()
read()
print("Line IO", time.time() - t)
# 网络请求密集型操作
t = time.time()
for x in range(10):
http_request()
print("Line Http Request", time.time() - t)
多线程: from threading import Thread
counts = [] //测试多线程并发执行CPU密集操作所需时间
t = time.time()
for x in range(10):
thread = Thread(target=count, args=(1,1))
counts.append(thread)
thread.start()
e = counts.__len__()
while True:
for th in counts:
if not th.is_alive():
e -= 1
if e <= 0:
break
print(time.time() - t)
def io(): //测试多线程并发执行IO密集操作所需时间
write()
read()
t = time.time()
ios = []
t = time.time()
for x in range(10):
thread = Thread(target=count, args=(1,1))
ios.append(thread)
thread.start()
e = ios.__len__()
while True:
for th in ios:
if not th.is_alive(): //判断所有线程是否执行完毕
e -= 1
if e <= 0://所有线程执行完毕
break
print(time.time() - t)
t = time.time() //测试多线程并发执行网络密集操作所需时间
ios = []
t = time.time()
for x in range(10):
thread = Thread(target=http_request)
ios.append(thread)
thread.start()
e = ios.__len__()
while True:
for th in ios:
if not th.is_alive():
e -= 1
if e <= 0:
break
print("Thread Http Request", time.time() - t)
多进程:from multiprocessing import Process
multiprocessing模块通过使用创建子进程,有效地避开了全局解释器锁(GIL),允许程序员充分利用机器上的多个处理器。
counts = [] //测试多进程并发执行CPU密集操作所需时间
t = time.time()
for x in range(10):
process = Process(target=count, args=(1,1))
counts.append(process)
process.start()
e = counts.__len__()
while True:
for th in counts:
if not th.is_alive():
e -= 1
if e <= 0:
break
print("Multiprocess cpu", time.time() - t)
t = time.time() //测试多进程并发执行IO密集型操作
ios = []
t = time.time()
for x in range(10):
process = Process(target=io)
ios.append(process)
process.start()
e = ios.__len__()
while True:
for th in ios:
if not th.is_alive():
e -= 1
if e <= 0:
break
print("Multiprocess IO", time.time() - t)
t = time.time() //测试多进程并发执行Http请求密集型操作
httprs = []
t = time.time()
for x in range(10):
process = Process(target=http_request)
ios.append(process)
process.start()
e = httprs.__len__()
while True:
for th in httprs:
if not th.is_alive():
e -= 1
if e <= 0:
break
print("Multiprocess Http Request", time.time() - t)