2017-06-18 88 views
1

我希望python执行(类似于subprocess.Popen()?)外部插座连接器后,我有另一个线程在socket.accept()阻止。使一个线程中的“socket.accept”在另一个线程中的某个代码之前执行? (python3)

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前行),不良后果会怎样。

没有线程的解决方案(我认为它首先肯定,因为线程是麻烦..)是不可能的,因为当我不能不打断subprocess.Popen()接受。

此外,我不想使用time.sleep(SOME_LARGE_VALUE),因为它也是不可控制的(容易出错,我使用正确的单词吗?),而且速度慢。

我已经了解到:Python3(CPython)具有全局解释器锁定(GIL)机制。有一次只有一个线程有执行的机会。如果一个线程阻塞(在本例中为socket.accept()),CPython将转向另一个线程。 (但是,这并没有帮助解决问题。)

任何人都知道强制执行命令的pythonic方式(或非pythonic方式)吗?

+0

“不良后果”?给我们一个提示怎么样?我可以看到想要在侦听之后进行调用,但子进程中接受的地方应该在哪里运行?在接受之前,在接受之后?为什么在子流程运行之前,accept会被阻塞? – tdelaney

+0

一旦'listen(1)'返回,TCP堆栈将在后台排队多达1个连接请求,即使你还没有调用accept。只要你在对方感到无聊并重置连接之前调用accept,它就会完成连接。 – tdelaney

+0

我会试一试......看起来我对套接字系统调用很不熟悉......:P – Thiner

回答

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

您已为您的特定情况提供了有效答案。但是,如果您更喜欢针对更通用的问题使用更通用的解决方案,那么实现这一点的方法很少。

然后我们来定义一个问题:我想安排一个准备一些资源给工作线程的工作,然后等待主线程中的资源准备就绪。资源的准备只会进行一次。当然,还有一个有效的问题:为什么我们不能在一个线程中按顺序运行所有的东西?但让我们认为这是向多线程世界介绍的练习。

所以,这里是一些骨架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 

尝试运行这段代码并观察CPU使用率(在Linux上使用top)。你会注意到它在等待期间长大。所以,请不要在现实生活中做这样的事情。

指定资源准备只完成一次。所以,工作线程可以准备数据,然后我们可以等待这个线程在主线程中完成。在这种确定的情况下,这将是我的首选解决方案。

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_sthprepare_sth线程之间共享。你可以玩argskwargs参数到start()方法。

+0

好吧,我认为一般情况是:当preparer线程就绪时,它本质上被阻塞(因此无法设置变量),并且preparer线程不应该事先设置变量,因为变量设置和准备好的线程不能被忽略。我应该如何处理这种情况? – Thiner

+0

请问您是否更具体一点,您的期望不适合锁定和条件变量计划? – ArturFH

相关问题