Python GIL 以及线程

1. GIL

GIL: Global Interpreter Lock 全局解释器锁.

每个 python process 都只创建唯一一个关键资源 GIL,当一个 thread 运行时,它必须获取此 GIL,运行结束再释放。

由于 GIL 唯一,因此任何时候,一个 process 中都不能有两个 thread 同时运行,一次只能运行一个 thread,这是由 python 的实现决定的,python 不善于以并行方式运行代码。

2. python 使用 thread 的意义: 减少等待时间

虽然 python 不支持两个 thread 并行执行,但是当程序需要等待时,使用线thread 可以缩短等待时间。

如果所有的 thread 都只是使用CPU,使用 thread 没有意义,因为时间不仅不会缩短,反而会因为 thread 频繁获取和释放 GIL 增加运行时间。

要创建线程,要先导入 threading 模块:

from threading import Thread

my_thread = Thread(target=my_func_name)
my_thread.start()

下面的程序有3个 thread: 主线程 main thread 以及 thread 1, thread 2
ask_user() 函数等待用户输入并输出,complex_calculation() 运行一项需要时间的计算:

import time
from threading import Thread

def ask_user():
    start = time.time()
    user_input = input("Enter your name: ")
    greet = f"Hello, {user_input}"
    print(greet)
    print(f'ask_user, {time.time() - start}')

def complex_calculation():
    start = time.time()
    print('Started calculating...')
    [x**2 for x in range(20000000)]
    print(f'complex_calculation, {time.time() - start}')

start = time.time()
ask_user()
complex_calculation()
print(f'Single thread total time: {time.time() - start}')

thread1 = Thread(target=complex_calculation)
thread2 = Thread(target=ask_user)

start = time.time()
thread1.start()
thread2.start()

thread1.join()
thread2.join()

print(f"Two threads total time: {time.time() - start} ")

输出如下:

Enter your name: user
Hello, user
ask_user, 2.0858802795410156
Started calculating...
complex_calculation, 3.8278822898864746
Single thread total time: 5.914762496948242
Started calculating...
Enter your name: user
Hello, user
ask_user, 1.4944937229156494
complex_calculation, 3.8242249488830566
Two threads total time: 3.8242249488830566 

Process finished with exit code 0

可见,使用线程,运行时间明显缩短,所用时间对比:

Single thread total time: 5.914762496948242
Two threads total time: 3.8242249488830566 

join() 方法使主线程等待,直到 thread1thread2 运行结束。

3. 线程池

使用模块 concurrent.futures 里的 ThreadPoolExecutor 可以创建 thread pool, 使用线程池比使用 Thread 逐个创建线程方便。 concurrent.futuresThreadPoolExecutor 十分流行。

import time
from concurrent.futures import ThreadPoolExecutor

def ask_user():
	pass


def complex_calculation():
	pass

start = time.time()
# max_workers=2  create 2 threads in this collection of threads
with ThreadPoolExecutor(max_workers=2) as pool:
    pool.submit(complex_calculation)
    pool.submit(ask_user)

# blocking operation like join(), because using with, below code no need to run
# pool.shutdown()

print(f"Two threads total time: {time.time() - start} ")

4. 不要杀线程!

不要杀线程,而是要等待,直到线程结束。

因为线程可能持有 GIL ,如果在没有释放之前线程就被杀掉,唯一的 GIL 会因此丢失,程序会因为其他线程在等待它们不可能重新获得的 GIL 而停止执行,陷入死锁。

如果一定要杀线程,就要手动释放 GIL ,这通常不是件很容易就能做到的事情,所以不要杀线程。

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值