1. 简介
操作系统能够运算调度的最小单位。
基本的执行流程如下:
- 一个.py文件就是一个主线程
- 它交给CPython解释器(Python3.5或2.7)进行解释
- 解释器交给OS进行物力资源调度让程序运行
2. 创建线程实例
import threading, time
def foo(obj):
for i in range(4):
print(obj, i)
time.sleep(1)
my_thred1 = threading.Thread(target=foo, args=('mythred1', ))
my_thred2 = threading.Thread(target=foo, args=('mythred2', ))
my_thred1.start()
my_thred2.start()
print('main thread is running...')
运行结果:
3. Cpython 只能调用单核举例(GIL)
import threading, time
def foo(obj):
start = time.time()
sum = 0
for i in range(100000000):
#print(obj, i)
sum += i
for i in range(100000000):
#print(obj, i)
sum += i
end = time.time()
print(obj, sum, end-start)
def foo1(obj):
start = time.time()
sum = 0
for i in range(100000000):
#print(obj, i)
sum += i
end = time.time()
print(obj, sum, end-start)
def foo2(obj):
start = time.time()
sum = 0
for i in range(100000000):
#print(obj, i)
sum += i
end = time.time()
print(obj, sum, end-start)
my_thred1 = threading.Thread(target=foo1, args=('mythred1', ))
my_thred2 = threading.Thread(target=foo2, args=('mythred2', ))
my_thred1.start()
my_thred2.start()
my_thred2.join()
my_thred1.join()
foo('主线程单独跑两次')
print('main thread is running...')
可以发现,在计算密集型任务中,其实两个线程一起跑比只开一个线程单独跑还要多费一倍时间。
3.1 GIL全局解释器锁
历史遗留问题,在同一时刻只能有一个线程进入解释器,因此无论OS调度哪个cpu核,只能处理一个线程。
为什么:在开发CPython解释器的时候,开发人员为了保证公共数据的安全性,现在所有的功能都是基于GIL开发的,因此无法改变,只有Cpython有这种问题。
解决途径:
- 利用进程
- 利用协程
- 线程部分交给C
3.2 解释串行比并发时间快一倍
因为处理的函数一直在运行,并未阻塞,解释器由于每一次只能调度一个线程,因此导致多线程切换也占用了时间。
两种形式的程序:
- IO密集型
- 计算密集型
因此在Python中更推崇在IO密集型的程序中使用线程,其实大部分程序都是IO密集型的
4. 进程
概念:一个程序的执行实例
与线程的区别:
- 子进程创建的时候,直接copy父进程的资源,因此开辟进程资源消耗量大
- 进程间无法直接通讯,因为处于不同的物理地址
- 线程间可以做到数据共享,进程不可以
- 线程间可以互相操作,进程不可以
- 主线程可影响子线程,主进程与子进程完全独立