![](/img/trans.png)
[英]Python program hangs when running socket.accept() in a different thread
[英]Make “socket.accept” in one thread execute before some code in another thread? (python3)
我想讓python執行一個外部套接字連接器(類似於subprocess.Popen()
嗎?)之后,我有另一個線程在socket.accept()
處socket.accept()
。
import socket
import threading
import subprocess
host = '0.0.0.0'
port = 3333
def getsock():
server_sock = []
def getsock_server():
sock = socket.socket()
sock.bind((host, port))
sock.listen(1)
accept_return = sock.accept() # *** CRITICAL ACCEPT ***
server_sock.append(accept_return[0])
address = accept_return[1]
return address
thr = threading.Thread(target=getsock_server)
thr.start()
"""Something that *must* be done after the CRITICAL ACCEPT
line is executing and the thread "thr" is blocked. Otherwise
the program malfunctions and blows into some undebuggable
complexity. ;(
Although it is a connect operation, it may not be as innocent
as belowing lines:
client_sock = socket.socket()
client_sock.connect((host, port))
"""
p = subprocess.Popen(
["./connector"], stdin=subprocess.PIPE, stdout=subprocess.PIPE)
thr.join()
return server_sock[0]
conn, addr = getsock()
基本上,我需要使所有工作像下面這樣才能進行:
1) thr.start()
2) sock.accept()
3) subprocess.Popen()
如果3)在2)之前,則將發生不良后果。
無緒的溶液(我認為這肯定是第一次,因為線程是麻煩..)是不可能的,因為當我socket.accept()
我不能只是subprocess.Popen()
而無需中斷接受。
我也不想使用time.sleep(SOME_LARGE_VALUE)
因為它也是不可控制的(容易出錯,我使用的單詞正確嗎?)而且速度很慢。
我了解到:Python3(CPython)具有全局解釋器鎖定(GIL)機制。 一次只有一個線程可以執行。 如果一個線程阻塞(在本例中為socket.accept()
),則CPython將轉向另一個線程。 (但是,這無助於解決此問題。)
任何人都知道執行命令的pythonic方式(或者不是Python的方式)嗎?
listen
告訴網絡堆棧開始在后台對傳入的連接請求進行排隊。 每個accept
請求的調用都會accept
隊列中的下一個請求。 看起來您的子進程想要連接回該程序。 聽完之后就叫它。
import socket
import threading
import subprocess
host = '0.0.0.0'
port = 3333
def getsock():
server_sock = []
sock = socket.socket()
sock.bind((host, port))
sock.listen(1)
p = subprocess.Popen(
["./connector"], stdin=subprocess.PIPE, stdout=subprocess.PIPE)
return sock.accept() # *** CRITICAL ACCEPT ***
conn, addr = getsock()
針對您的具體情況,已為您提供了有效的答案。 但是,如果您希望使用更通用的解決方案來解決更通用的問題,則可以通過幾種不同的方法來實現。
然后讓我們定義一個問題:我想安排一個工作,為工作線程准備一些資源,然后在主線程中等待資源准備就緒。 資源的准備只會進行一次。 當然,仍然存在一個有效的問題:為什么我們不能在一個線程中按順序運行所有事物? 但是,讓我們將此作為介紹多線程世界的一種練習。
因此,這是一些基本的python代碼:
import threading
import time
import random
data_ready=False
def do_sth():
global data_ready
def prepare_sth():
global data_ready
print("preparing, simulated by random wait")
time.sleep(random.randrange(5,10))
data_ready=True
print("resource is ready")
print("do_sth");
thr = threading.Thread(target=prepare_sth)
thr.start()
# WAIT HERE
if data_ready:
print("OK")
else:
print("ERROR")
do_sth()
當然,它不能按預期方式工作,並且輸出中的某處會出現ERROR
消息。 但是我們可以將問題更改為:用什么代替WAIT HERE
的WAIT HERE
?
解決此類問題的最明顯和最糟糕的方法是主動等待:
while not data_ready:
pass
嘗試運行此代碼並觀察(在Linux上使用top
)CPU使用率。 您會注意到它在等待期間長大。 因此,請不要在現實生活中做此類事情。
指定僅准備一次資源。 因此,工作線程可以准備數據,然后我們可以等待該線程在主線程中完成。 在這種定義的情況下,這將是我的首選解決方案。
thr.join()
最后,使用完整的鎖定和條件變量方案。 它需要更多的更改,因此將完整的代碼粘貼到此處:
import threading
import time
import random
data_ready=False
def do_sth():
global data_ready
lock=threading.Lock()
cond=threading.Condition(lock)
def prepare_sth():
global data_ready
with cond:
print("preparing, simulated by random wait")
time.sleep(random.randrange(5,10))
data_ready=True
print("resource is ready")
cond.notify()
print("do_sth");
with cond:
thr = threading.Thread(target=prepare_sth)
thr.start()
while not data_ready:
print("waiting")
cond.wait()
if data_ready:
print("OK")
else:
print("ERROR")
do_sth()
如果您需要在一個線程中以循環方式准備資源(例如,一些數據),然后在另一個線程中使用它,則將是一種正確的方法。 請尋找生產者-消費者模型 。
最后但並非最不重要。 我已經為data_ready
變量使用了一個global
說明data_ready
,因為我很懶,並且這個示例的內容有所不同。 但是,請將其視為錯誤的設計。 該變量應僅在do_sth
和prepare_sth
線程之間共享。 您可以在start()
方法中使用args
和kwargs
參數。
聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.