簡體   English   中英

使一個線程中的“ socket.accept”在另一個線程中的某些代碼之前執行? (python3)

[英]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 HEREWAIT 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_sthprepare_sth線程之間共享。 您可以在start()方法中使用argskwargs參數。

暫無
暫無

聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.

 
粵ICP備18138465號  © 2020-2024 STACKOOM.COM