如果我们有GIL,为什么我们需要线程锁?

2024-07-08 14:42:50 发布

您现在位置:Python中文网/ 问答频道 /正文

我相信这是个愚蠢的问题,但我还是找不到。实际上,最好把它分成两个问题:

1)我们可以有很多线程,但是由于GIL,在一瞬间只有一个线程在执行,这是对的吗?在

2)如果是,为什么我们还需要锁?我们使用锁来避免两个线程读写共享对象的情况,因为GIL-twi线程不能在一瞬间执行,对吗?在


Tags: 对象情况线程giltwi
3条回答

GIL防止多个线程同时执行,但不是在所有情况下。在

GIL在线程执行的I/O操作期间临时释放。也就是说,多个线程可以同时运行。这就是你还需要锁的原因之一。在

我不知道我在哪里找到这个参考。。。。在一个视频或其他东西-很难查找,但你可以进一步调查自己

在任何时候,是的,只有一个线程在执行Python代码(其他线程可能正在执行一些IO、NumPy等等)。这基本上是真的。然而,这在任何单处理器系统上都是如此,然而人们在单处理器系统上仍然需要锁。在

请查看以下代码:

queue = []
def do_work():
    while queue:
        item = queue.pop(0)
        process(item)

只有一根线,一切都很好。对于两个线程,您可能会从queue.pop()得到异常,因为另一个线程首先对最后一个项目调用了queue.pop()。所以你得想办法解决这个问题。使用锁是一个简单的解决方案。您也可以使用适当的并发队列(比如queue模块),但是如果您查看queue模块内部,您会发现Queue对象中有一个threading.Lock()。所以不管怎样你都在用锁。在

在没有必要的锁的情况下编写多线程代码是一个常见的新手错误。你看了代码,然后想,“这会很好的工作”,然后在几个小时后发现,一些真正奇怪的事情发生了,因为线程没有正确地同步。在

或者简而言之,在多线程程序中有许多地方需要防止另一个线程修改结构,直到应用完一些更改为止。这允许您在数据上维护不变量,如果您不能维护不变量,那么基本上不可能编写正确的代码。在

或者用最短的方式说,“如果你不在乎你的代码是否正确,你就不需要锁。”

吉尔保护着Python的内部。这意味着:

  1. 您不必担心由于多线程而导致解释器出错
  2. 大多数事情并不是真正并行运行的,因为python代码是按顺序执行的,这是由于GIL

但是GIL不保护你自己的代码。例如,如果您有以下代码:

self.some_number += 1

它将读取self.some_number的值,计算some_number+1,然后将其写回self.some_number。在

如果在两个线程中执行此操作,则一个线程和另一个线程的操作(读、加、写)可能会混合在一起,因此结果是错误的。在

这可能是执行顺序:

  1. thread1读取self.some_number(0)
  2. 线程2读取self.some_number(0)
  3. thread1计算some_number+1(1)
  4. thread2计算some_number+1(1)
  5. thread1将1写入self.some_number
  6. thread2将1写入self.some_number

您可以使用锁强制执行以下执行顺序:

  1. thread1读取self.some_number(0)
  2. thread1计算some_number+1(1)
  3. thread1将1写入self.some_number
  4. 线程2读取self.some_number(1)
  5. thread2计算some_number+1(2)
  6. thread2将2写入self.some_number

编辑:让我们用一些代码来完成这个答案,这些代码显示了所解释的行为:

^{pr2}$

有两个函数实现增量。一个用锁,另一个不用。在

函数increment_in_x_threads在许多线程中实现递增函数的并行执行。在

现在,使用足够多的线程运行它,几乎可以肯定会发生错误:

print('unsafe:')
increment_in_x_threads(70, increment_n_times, 100000)

print('\nwith locks:')
increment_in_x_threads(70, safe_increment_n_times, 100000)

在我的例子中,它打印了:

unsafe:
finished in 0.9840562343597412s.
total: 4654584
expected: 7000000
difference: 2345416 (33.505942857142855 %)

with locks:
finished in 20.564176082611084s.
total: 7000000
expected: 7000000
difference: 0 (0.0 %)

因此,如果没有锁,就会出现很多错误(33%的增量失败)。另一方面,加上锁,速度慢了20倍。在

当然,这两个数字都被放大了,因为我使用了70个线程,但这显示了总体思路。在

相关问题 更多 >

    热门问题