实战篇一
Python 并发、并行、同步和异步——学习笔记
概念解析
-
串行:同一个时间段只干一件事
-
并行:同一个时间段可以干多件事
-
并发 V.S. 并行
- 并发是指一个时间段内,有几个程序在同一个CPU上运行,但是任意时刻只有一个程序在CPU上运行(单核切换运行)。
- 并行是指任意时刻点上,有多个程序同时运行在多个CPU上,即每个CPU独立运行一个程序(多核同时运行)。
- 并行的最大数量和CPU的数量是一致的。
-
同步 V.S. 异步
- 同步是指代码调用IO操作时,必须等待IO操作完成返回才调用的方式
- 异步是指代码调用IO操作时,不必等待IO操作完成返回调用的方式
-
多线程:交替执行,另一种意义上的串行 比进程的概念小,减少了进程切换过程中的消耗
-
多进程:并行执行,真正意义上的并行 通过切换不同的进程来实现多任务的并行
-
多进程 V.S. 多线程
- 由于全局解释锁GIL的存在,Python的多线程无法利用多核优势,所以不适合计算密集型任务,适合IO密集型任务。
- CPU密集型任务适合使用多进程
- 进程切换代价要高于线程
- 线程之间可以通过全局变量来进行通信,但是进程不行。进程之间的数据是完全隔离的。
-
threading类:
threading.Lock()
:同步锁(互斥锁),解决数据安全问题threading.RLock()
:递归锁,解决线程死锁问题threading.Semaphore()
:信号量,最多允许同时有n个线程,超过信号量的线程会被堵塞,直到有线程的信号量被释放。threading.Event()
:事件,让不同的线程保持同步而不是独立的运行,彼此交互:你给我发一个信号,我接到了执行一个操作,再给你发一个信号,你接到了执行操作并继续发信号……
锁
import threading
#生成锁对象,全局唯一
lock = threading.Lock()
#获取锁
lock.acquire()
#释放锁
lock.release()
- 可以使用上下文管理协议来管理锁
lock = threading.Lock()
with lock:
pass
#等价于:
lock.acquire()
try:
pass
finally:
lock.release()
- 加锁会影响性能,获取锁和释放锁都需要时间。
- 使用锁可能会引起死锁
多线程
import threading
import time
def sleeper(n,name):
print('Hi, I`m {}. I`m going to sleep 5 seconds'.format(name))
time.sleep(n)
print('{} has woken up from sleep'.format(name))
e.g.1
def main():
t = threading.Thread(target = sleeper, args = (5,'thread1'))
t.start()
#调用.join()方法来阻塞当前线程,直到该线程结束后,才跳转执行其余的线程
#t.join() 如果加上这个语句,程序需要等整个sleeper执行完毕后才会跳回执行后面的两次print
print('hello')
print('hello')
>>
Hi, I`m thread1. I`m going to sleep 5 seconds.
hello
hello
thread1 has woken up from sleep.
start和join正确的使用方式
# 这个是正确的使用方式
# 将start和join的方式进行分开循环
import threading
import time
def sleeper(n,name):
print('Hi, I`m {}. I`m going to sleep 5 seconds'.format(name))
time.sleep(n)
print('{} has woken up from sleep'.format(name))
e.g.2
def main():
start = time.time()
thread_list = []
for i in range(1,6):
t = threading.thread(target=sleeper, args=(5,'thread{}'.format(i)))
thread_list.append(t)
t.start()
print('{} has started.'.format(t.name)
# join的使用
for t in thread_list:
t.join()
end = time.time()
print('total time: {}'.format(end - start))
print('aaa')
>>
Hi, I`m thread1. I`m going to sleep 5 seconds
Thread-1 has started.
Hi, I`m thread2. I`m going to sleep 5 seconds
Thread-2 has started.
Hi, I`m thread3. I`m going to sleep 5 seconds
Thread-3 has started.
Hi, I`m thread4. I`m going to sleep 5 seconds
Thread-4 has started.
thread2 has woken up from sleep
thread1 has woken up from sleep
thread4 has woken up from sleep
thread3 has woken up from sleep
total time: 5.001285791397095
aaa
start和join错误的使用方式
错误的原因:将start和join的方式进行放在一起,错误的根本的原因是:当我们的第一个线程thread1启动了之后,直接进入了join,主程序进入等待,要等待thread1运行结束之后才能完成主程序下面的运行,在次进入循环,此时thread2.start(),直接又进入thread2.join,主程序又要等thread2运行结束,在开始thread3,这样的话,我们的程序在逻辑上又变成单线程了,所以在thread.start()后面直接接join的方式是错误的方式
# 这个是错误的使用方式
import threading
import time
def sleeper(n,name):
print('Hi, I`m {}. I`m going to sleep 5 seconds'.format(name))
time.sleep(n)
print('{} has woken up from sleep'.format(name))
e.g.3
def main():
start = time.time()
thread_list = []
for i in range(1,6):
t = threading.thread(target=sleeper, args=(5,'thread{}'.format(i)))
thread_list.append(t)
t.start()
t.join() #阻塞进程
print('{} has started.'.format(t.name)
end = time.time()
print('total time: {}'.format(end - start))
print('aaa')
>>
Hi, I`m thread1. I`m going to sleep 5 seconds
thread1 has woken up from sleep
Thread-1 has started.
Hi, I`m thread2. I`m going to sleep 5 seconds
thread2 has woken up from sleep
Thread-2 has started.
Hi, I`m thread3. I`m going to sleep 5 seconds
thread3 has woken up from sleep
Thread-3 has started.
Hi, I`m thread4. I`m going to sleep 5 seconds
thread4 has woken up from sleep
Thread-4 has started.
total time: 20.00114393234253
aaa