【问题标题】:How GIL works with Multi threaded server?GIL 如何与多线程服务器一起工作?
【发布时间】:2017-07-12 20:10:50
【问题描述】:

在 CPython 环境中,一个线程不会从另一个线程获取 GIL,除非它被阻塞(比如使用 sleep())或在 IO 上

在下面的服务器代码中,

# server.py
import socket
import sys
from threading import Thread

def echoHandler(conn, addr):
    try:
        while True:
            data = conn.recv(16) # Blocking call
            if data:
                print(data.decode('utf-8'))
                conn.sendall(data)
            else:
                break
    finally:
        print('Closing the connection from server')
        conn.close()

if __name__ == '__main__':
    sock = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
    server_address = ('localhost', 10006)
    sock.bind(server_address)
    sock.listen(1)
    while True:
        conn, addr = sock.accept()
        t = Thread(target=echoHandler, args=(conn, addr))
        t.daemon = True
        t. start()
        print('Waiting for another conn')

#client.py
import socket
import pdb

# pdb.set_trace()
if __name__ == '__main__':
    sock = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
    server_address = ('localhost', 10006)
    print('connecting to', server_address)
    sock.connect(server_address)

    while True:
        try:
            message = input().encode('utf-8')
        except EOFError:  #EAFP
            print('Breaking ')
            break
        sock.sendall(message)
        data = sock.recv(16) # Blocking call
        print(data.decode('utf-8'))
    print('close the socket')
    sock.close()

问题:

一个线程什么时候有机会从另一个线程获取 GIL 来服务客户端?是conn.recv(16),允许服务器运行多线程吗?

【问题讨论】:

  • “在 CPython 环境中,一个线程不会从另一个线程获取 GIL,除非它被阻塞(比如使用睡眠)或在 IO 上” - 是什么让你这么认为?
  • 等待网络,这是此类服务器通常所做的大部分工作,正在在 IO 上被阻止。
  • @user2357112 示例 1: this 程序在没有 sleep() 的情况下作为顺序代码工作。 end-start 输出告诉 示例 2: 以上代码在查询中,线程 1 在 recv() 上释放 GIL。这让我觉得
  • 打印的end-start 值并不意味着程序是按顺序执行的,当然也不意味着 GIL 释放仅在线程被阻塞或执行 I/O 时发生。
  • @user2357112 t1 CPU 绑定线程占用提供的 CPU 时间片并且永远不会放弃 GIL,直到它完成,或者直到没有操作码完成执行。然后 t2 占用提供的 CPU 时间片。主线程无论如何都被阻塞了。这就是我所说的顺序

标签: python multithreading gil


【解决方案1】:

是的,在conn.recv(16)(毕竟是网络 I/O)期间不会持有 GIL。

它还定期发布(2010 年之前,每 100 个操作码;最近,以可配置的 5 毫秒间隔)以避免线程饥饿。

【讨论】:

  • 这取决于操作系统调度程序。 Python 中没有任何一种方式强制执行。
  • @CharlesDuffy 这个5ms的可配置间隔在源代码的什么地方,你知道吗?
  • 因为here,他说,GIL 阻止了多核并发。
  • @overexchange,确实如此(大多数情况下,有一些例外——例如在执行不与本机 Python 数据结构交互的 CPU 密集型操作之前显式释放 GIL 的 C 库),但这只是因为您没有同时使用多个核心并不意味着您没有使用多个核心。核 1 上的时间片和核 2 上的时间片一个接一个不是并发的,但仍然是两个不同核上的时间片。
  • 我如何验证这一点?
猜你喜欢
  • 1970-01-01
  • 1970-01-01
  • 2014-11-30
  • 1970-01-01
  • 1970-01-01
  • 2014-08-06
  • 2020-07-31
  • 2015-01-29
  • 2014-01-11
相关资源
最近更新 更多