单核 CPU 是如何执行任务的呢?操作系统轮流让各个任务交替执行,任务 1 执行 0.01s,切换到任务 2,任务 2 执行 0.01s,再切换到任务 3,这样反复执行下去。表面上看,每个任务都是交替执行的,但是,由于 CPU 的执行速度实在太快了,我们感觉就像所有任务在同时执行一样。
真正的并行执行多任务只能在多核 CPU上实现,但是,由于任务数量远远多于 CPU 和核心数量,所以,操作系统也会自动把很多任务轮流调度到每个核心上执行。
对于操作系统来说,一个任务就是一个进程 Process,比如打开一个浏览器就是启动一个浏览器进程。在一个进程内部,要同时干多件事情,就需要同时运行多个子任务,我们把进程内的这些子任务成为线程 Thread。
由于每个进程至少要干一件事,所以,一个进程至少有一个线程。当然,像 Word 这种复杂的进程可以有多个线程,多个线程可以同时执行,多线程的执行方式和多线程是一样的,也是由操作系统在多个线程之间快速切换,让每个线程都短暂地交替运行,看起来就像同时执行一样。当然,真正地同时执行多线程需要多核 CPU 才可能实现。
多任务的实现有 3 种方式:
- 多进程模式
- 多线程模式
- 多进程 + 多线程模式
多进程
multiprocessing 模块提供了一个 Process 类来代表一个进程对象。
from multiprocessing import Process import os def run_proc(name): print('Run child process %s (%s)...' % (name, os.getpid())) if __name__ == '__main__': print('Parent process %s.' % os.getpid()) p = Process(target=run_proc, args=('test',)) print('Child process will start.') p.start() p.join() print('Child process end.')
多线程
启动一个线程就是把一个函数传入并创建 Thread 实例,然后调用 start( ) 开始执行。
import time, threading def loop(): print('thread %s is running...' % threading.current_thread().name) n = 0 while n < 5: n = n + 1 print('thread %s >>> %s' % (threading.current_thread().name, n)) time.sleep(1) print('thread %s ended.' % threading.current_thread().name) if __name__ == "__main__": print('thread %s is running...' % threading.current_thread().name) t = threading.Thread(target=loop, name='LoopThread') t.start() t.join() print('thread %s ended.' % threading.current_thread().name)
线程锁
balance = 0 lock = threading.Lock() def run_thread(n): for i in range(100000): # 先要获取锁 : lock.acquire() try: # 放心地改吧 : change_it(n) finally: # 改完了一定要释放锁 : lock.release()
Python 的线程虽然是真正的线程,但解释器执行代码时,有一个 GIL 锁:Global Interpreter Lock,任何 Python 线程执行前,必须先获得 GIL 锁,然后,每执行 100 条字节码,解释器就自动释放 GIL 锁,让别的线程有机会执行。这个全局 GIL 全局锁实际上把所有线程的执行代码都给上了锁,所以,多线程在 Python 中只能交替执行,即使 100 个线程跑在 100 核 CPU 上,也只能用到 1 个核。
非原创,原文来源廖雪峰 Python 教程。