我希望python执行(类似于subprocess.Popen()
?)外部套接字连接器我有另一个线程阻止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方式(或不那么pythonic方式)?
答案 0 :(得分:1)
listen
告诉网络堆栈开始在后台排队传入的连接请求。每次调用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()
答案 1 :(得分:1)
为您的具体案例提供了有效答案。但是,如果您更喜欢更通用的解决方案来解决更通用的问题,那么实现这一目标的方法很少。
然后让我们定义一个问题:我想安排一个为工作线程准备一些资源的工作,然后在主线程中等待资源准备好。资源的准备只进行一次。当然,还有一个有效的问题:为什么我们不能在一个线程中顺序运行所有事情?但是让我们把它看作是向多线程世界介绍的练习。
所以,这是一些骨架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
的内容?
解决此类问题的最明显和最糟糕的方法是积极等待:
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()
如果您需要在一个线程中以循环方式准备资源(例如,某些数据),并在另一个线程中使用它,那么这将是一种正确的方法。请查看生产者 - 消费者模型。
最后但并非最不重要。我为global
变量使用了data_ready
说明符,因为我很懒,这个例子是关于不同的东西。但是,请将其视为糟糕的设计。该变量应仅在do_sth
和prepare_sth
个线程之间共享。您可以将args
和kwargs
参数与start()
方法一起使用。