让一个线程中的“socket.accept”在另一个线程中的某些代码之前执行? (python3)

时间:2017-06-18 14:20:42

标签: python multithreading sockets

我希望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方式)?

2 个答案:

答案 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_sthprepare_sth个线程之间共享。您可以将argskwargs参数与start()方法一起使用。