python:socket.sendall()hogs GIL?

时间:2016-09-22 04:33:05

标签: python multithreading sockets deadlock hang

我有一个多线程程序(大约20个线程;生成器/消费者混合有很多队列)

在其中一个线程中,它从队列中弹出字符串并将其发送到远程程序

# it starts the thread like this
workQ = Queue.Queue()
stop_thr_event = threading.Event()
t = threading.Thread( target=worker, args=(stop_thr_event,) )


# in the thread's worker function
def worker(stop_event):
    sock = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
    server_address = (myhost, int(myport))
    sock.connect(server_address)
    while True:
        try:
            item = workQ.get(timeout=1)

            if print_only:
                print item
            else:
                if item.startswith("key:"):
                    item = "{%s}" % item
                    sock.sendall(item)

            workQ.task_done()
        except Queue.Empty, msg:
            if stop_event.isSet():
                break

间歇性地,我的程序将挂起,没有任何线程正在做任何工作

经过反复试验,我发现我的程序只挂起了这个运行的线程

我唯一的猜测是sendall()正在占用GIL而我的整个程序都挂起了

1)这是否是一个看似合理的理论? 2)如果我的理论是正确的,我能做什么让sendall()不会占用GIL?使它成为非阻塞发送?

2 个答案:

答案 0 :(得分:2)

你错了。没有网络活动持有GIL,sendall()也不例外!

item=workQ.get()
socket.sendall() **# may take long time here.**
workQ.task_done() 

因为sendall()可能需要很长时间,而其他使用workQ 的线程在调用task_done()之前无法轮流运行 ==>这就是为什么你的整个程序似乎都处于悬空状态。

答案 1 :(得分:1)

GIL-hogging不会导致程序挂起。它可能会损害程序的性能,但这与悬挂相去甚远。您更有可能遇到某种形式的deadlock。 GIL不能参与死锁,因为解释器不断释放并重新获取它,获取或释放GIL通常不依赖于获取或释放任何其他资源,其他锁也不依赖于GIL。

你对stop_thr_event锁的使用是相当特殊的。对于主人来说,简单地放一系列"我们已经完成,回家"对象进入队列,并让工作人员检测这些对象并在识别时返回。这也与经验法则相关,timeout的唯一正确值是零和无穷大(即没有超时)。在当前情况下,您的工作人员正在等待一秒钟,检查事件,等待一秒钟等等,并polling is a Bad Thing

现在,如果通过"挂起"你的意思是程序在恢复之前偶尔冻结很短的时间, 表现不佳,所以也许GIL可能是罪魁祸首。但是套接字不是问题。问题是你可能有大量的线程竞争GIL(因为他们都试图每秒轮询一次),如果你仍然在2.x,你就不会这样做。有the new GIL。消除民意调查将有助于此。