据我所知,GIL使得线程不可能单独利用一个核心。这是一个基本的问题,但是,threading库的意义何在呢?如果线程化代码的速度与普通程序相当,那么threading库似乎毫无用处。
threading
q3qa4bjr1#
在某些情况下,应用程序甚至可能无法充分利用一个内核,使用线程(或进程)可能有助于实现这一点。考虑一个典型的Web应用程序。它接收来自客户端的请求,对数据库执行一些查询,然后将数据返回给客户端。假定IO操作在大多数时间内都比CPU操作慢一个数量级,则此类应用程序会等待IO完成。首先,它等待从套接字读取请求,然后等待直到对数据库的请求被写入到对DB打开的套接字中。然后,它等待来自数据库的响应,然后等待将响应写入客户端套接字。等待IO完成可能需要90%(或更多)的时间来处理请求。当单线程应用程序等待IO时,它只是不使用内核,而内核可用于执行。因此,此类应用程序即使在单核上也有空间供其他线程执行。在这种情况下,当一个线程等待IO完成时,它会释放GIL,而另一个线程可以继续执行。
xeufq47z2#
严格来说,CPython支持多io绑定线程+单cpu绑定线程。
*I/O绑定方法:file.open、file.write、file.read、socket.send、socket.recv等。当Python调用这些I/O函数时,它将释放GIL,并在I/O函数 * 隐式 * 返回后获取GIL。*CPU限制方法:算术计算等。*C扩展方法:方法必须 * 显式 * 调用PyEval_SaveThread和PyEval_RestoreThread,以告诉Python解释器您正在做什么。
file.open
file.write
file.read
socket.send
socket.recv
PyEval_SaveThread
PyEval_RestoreThread
rslzwgfq3#
尽管存在GIL,但 threading 库仍能很好地工作。在我解释之前,你应该知道Python的线程是真实的的线程-它们是运行Python解释器的普通操作系统线程,GIL(或全局解释器锁)只在运行纯Python代码时才被使用,而且在许多情况下是完全释放的,甚至不检查。GIL不会阻止这些操作并发运行:1.IO操作,如发送和接收网络数据或阅读文件。1.繁重的内置CPU绑定操作,如散列或压缩。1.一些C扩展操作,如numpy计算。其中任何一个(以及更多)都可以以并发的方式运行得很好,而且在大多数程序中,这些是占用时间最长的较重部分。用Python构建一个示例API来获取天文数据并计算轨道意味着:
基本上,GIL不会影响程序运行时的绝大部分时间。此外,至少在网络方面,其他方法现在更流行,比如asyncio,它在同一个线程上提供协作式多任务处理,有效地消除了线程过载的缺点,并允许同时运行更多的连接。GIL可能是个问题,在运行纯Python代码的CPU密集型程序(例如计算Fibonacci数列的简单程序)中,它会使线程变得毫无用处。但在大多数真实的情况下,除非您运行的是规模巨大的网站,如Youtube(无可否认,它遇到了问题),否则GIL不是一个重要的问题。
asyncio
s3fp2yjn4#
请阅读以下内容:https://opensource.com/article/17/4/grok-gil这里有两个概念:1.协作式多任务处理:当一个线程执行i/o绑定任务时,它放弃GIL上的锁,以便其他线程可以继续。1.抢占式多任务处理:基本上每个线程都会运行一段时间(执行的字节码数量或时间),然后它会放弃锁,以便其他线程继续运行。因此,当一个线程同时运行时,(1)意味着我们仍然在最有效地利用内核-请注意,这对CPU受限的工作负载没有帮助。(2)意味着每个线程都分配了相当数量的CPU时间。
4条答案
按热度按时间q3qa4bjr1#
在某些情况下,应用程序甚至可能无法充分利用一个内核,使用线程(或进程)可能有助于实现这一点。
考虑一个典型的Web应用程序。它接收来自客户端的请求,对数据库执行一些查询,然后将数据返回给客户端。假定IO操作在大多数时间内都比CPU操作慢一个数量级,则此类应用程序会等待IO完成。首先,它等待从套接字读取请求,然后等待直到对数据库的请求被写入到对DB打开的套接字中。然后,它等待来自数据库的响应,然后等待将响应写入客户端套接字。
等待IO完成可能需要90%(或更多)的时间来处理请求。当单线程应用程序等待IO时,它只是不使用内核,而内核可用于执行。因此,此类应用程序即使在单核上也有空间供其他线程执行。
在这种情况下,当一个线程等待IO完成时,它会释放GIL,而另一个线程可以继续执行。
xeufq47z2#
严格来说,CPython支持多io绑定线程+单cpu绑定线程。
*I/O绑定方法:
file.open
、file.write
、file.read
、socket.send
、socket.recv
等。当Python调用这些I/O函数时,它将释放GIL,并在I/O函数 * 隐式 * 返回后获取GIL。*CPU限制方法:算术计算等。
*C扩展方法:方法必须 * 显式 * 调用
PyEval_SaveThread
和PyEval_RestoreThread
,以告诉Python解释器您正在做什么。rslzwgfq3#
尽管存在GIL,但 threading 库仍能很好地工作。
在我解释之前,你应该知道Python的线程是真实的的线程-它们是运行Python解释器的普通操作系统线程,GIL(或全局解释器锁)只在运行纯Python代码时才被使用,而且在许多情况下是完全释放的,甚至不检查。
GIL不会阻止这些操作并发运行:
1.IO操作,如发送和接收网络数据或阅读文件。
1.繁重的内置CPU绑定操作,如散列或压缩。
1.一些C扩展操作,如numpy计算。
其中任何一个(以及更多)都可以以并发的方式运行得很好,而且在大多数程序中,这些是占用时间最长的较重部分。
用Python构建一个示例API来获取天文数据并计算轨道意味着:
基本上,GIL不会影响程序运行时的绝大部分时间。
此外,至少在网络方面,其他方法现在更流行,比如
asyncio
,它在同一个线程上提供协作式多任务处理,有效地消除了线程过载的缺点,并允许同时运行更多的连接。GIL可能是个问题,在运行纯Python代码的CPU密集型程序(例如计算Fibonacci数列的简单程序)中,它会使线程变得毫无用处。但在大多数真实的情况下,除非您运行的是规模巨大的网站,如Youtube(无可否认,它遇到了问题),否则GIL不是一个重要的问题。
s3fp2yjn4#
请阅读以下内容:https://opensource.com/article/17/4/grok-gil这里有两个概念:
1.协作式多任务处理:当一个线程执行i/o绑定任务时,它放弃GIL上的锁,以便其他线程可以继续。
1.抢占式多任务处理:基本上每个线程都会运行一段时间(执行的字节码数量或时间),然后它会放弃锁,以便其他线程继续运行。因此,当一个线程同时运行时,(1)意味着我们仍然在最有效地利用内核-请注意,这对CPU受限的工作负载没有帮助。(2)意味着每个线程都分配了相当数量的CPU时间。