Python多线程(一):GIL

转载自:https://www.jianshu.com/p/fb81d5570f05

GIL 是 Global Interpreter Lock,即全局解释锁的缩写,保证了了同一时刻只有一个线程在一个 CPU 上执行字节码,无法将多个线程映射到多个 CPU 上。这是 CPython 解释器的缺陷,由于 CPython 是大部分环境下默认的 Python 执行环境,而很多库都是基于 CPython 编写的,因此很多人将 GIL 归结为 Python 的问题。

GIL 被设计来保护线程安全,由于多线程共享变量,如果不能很好的进行线程同步,多线程非常容易将线程改乱。实际上即使有了 GIL,这个问题也无法完全解决,因为 GIL 实际上也会释放,而且它并不是在某个线程执行完成后才释放,而是根据代码的字节码或者时间片进行释放,下面是一个例子:

import threading

total = 0
def add():
    global total
    for i in range(1000000):
        total += 1

def desc():
    global total
    for i in range(1000000):
        total -= 1

thread1 = threading.Thread(target=add)
thread2 = threading.Thread(target=desc)
thread1.start()
thread2.start()
thread1.join()
thread2.join()

print(total)

这个程序直观来看,是将total10000001000000,不管哪个线程先执行,最后的结果应该都是0才对,但是如果允许你该上面的代码多次,就会发现每次代码的结果都不一样,有正有负。这其中的原因就涉及到了 GIL 的释放。我们首先可以查看一下普通加法函数的字节码:

import dis
def add1(a):
    a += 1
    return a
print(dis.dis(add1))

结果如下:


  2           0 LOAD_FAST                0 (a)
              2 LOAD_CONST               1 (1)
              4 INPLACE_ADD
              6 STORE_FAST               0 (a)

  3           8 LOAD_FAST                0 (a)
             10 RETURN_VALUE
None

可以看到a += 1的执行过程是先将变量a装载进 CPU,再将常量1装载进 CPU,然后执行相加操作,最后再将a存储在内存中。由于 GIL 不是根据 Python 代码段来释放,而是根据字节码或者时间片来释放的,在之前的例子中,如果add函数在进行加法后还未在内存中保存,GIL 释放,desc函数获得执行权,此时它进行装载时装载的变量total是未进行加法操作的total,因此相当于之前的add函数失去了作用,在进行多次循环后,程序的运行结果自然不为0。这种情况称为竞态条件(race condition),即使没有 GIL,也会出现这种问题。解决方法是使用锁机制,将会在后面的文章中提到。

还有一种条件会导致 GIL 释放,那就是当程序遇到 IO 操作和time.sleep将程序阻塞的时候,因此多线程对于处理IO操作的问题非常有效。

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
Python多线程是一种并发编程技术,可以同时执行多个线程,以提高程序的运行效率。在Python中,可以使用`threading`模块来实现多线程。 下面是一个简单的Python多线程示例: ```python import threading def worker(): print("Worker is running") # 创建线程对象 thread1 = threading.Thread(target=worker) thread2 = threading.Thread(target=worker) # 启动线程 thread1.start() thread2.start() # 等待所有线程结束 thread1.join() thread2.join() ``` 在上面的示例中,我们定义了一个`worker`函数,它会在控制台输出一条消息。然后我们创建了两个线程对象,并使用`start()`方法启动它们。最后,我们使用`join()`方法等待所有线程结束。 需要注意的是,多线程Python中并不一定能够实现真正的并行执行,因为PythonGIL(全局解释器锁)机制限制了多线程的执行效率。这意味着即使在多个线程中同时执行相同的代码,也只有一个线程可以获得CPU资源进行执行。但是,Python多线程对于某些特定的任务仍然是非常有用的,例如I/O密集型任务或者使用多核CPU的系统。 在Python中学习多线程时,需要了解以下几点: 1. 线程的创建和启动:需要使用`Thread`类来创建线程对象,并使用`start()`方法来启动线程。 2. 线程的同步:由于GIL机制的存在,Python多线程并不能实现真正的并行执行。因此,需要使用锁、条件变量等机制来保证线程之间的同步和通信。 3. 线程池:可以使用线程池来管理多个线程,以提高程序的运行效率。Python中的`queue`模块提供了线程安全的队列,可以用于实现线程池。 4. 多进程:如果需要更高效的并发编程,可以使用Python的多进程模块`multiprocessing`。它可以更好地利用多核CPU的优势,并避免GIL的影响。 5. 锁的使用:在使用多线程时,需要使用锁来保证线程之间的同步和通信。需要注意避免死锁和竞争条件等问题。 6. 死锁问题:死锁是线程之间相互等待资源导致的问题,可以通过适当的调度策略和使用锁来避免死锁问题的发生。 7. 多线程的优点和缺点:多线程适用于I/O密集型任务和需要并发执行的任务。但是,它也存在一些缺点,如性能开销、资源竞争等问题。需要根据具体的应用场景来选择是否使用多线程。 总之,Python多线程是一种重要的并发编程技术,可以用于提高程序的运行效率。在学习Python多线程时,需要了解其基本原理和常见问题,并根据具体的应用场景来选择是否使用多线程

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值