【问题标题】:Python: How to interrupt raw_input() in other threadPython:如何在其他线程中中断 raw_input()
【发布时间】:2014-11-16 20:13:23
【问题描述】:

我正在用 python 编写一个简单的客户端-服务器程序。在客户端程序中,我创建了两个线程(使用 Python 的线程模块),一个用于接收,一个用于发送。接收线程不断地从服务器端接收字符串;而发送线程持续监听用户输入(使用 raw_input())并将其发送到服务器端。两个线程使用 Queue 进行通信(这是本机同步的,LIKE!)。

基本逻辑如下:

接收线程:

global queue = Queue.Queue(0)

def run(self):
    while 1:
        receive a string from the server side
        if the string is QUIT signal:
            sys.exit()
        else:
            put it into the global queue

发送线程:

def run(self):
    while 1:
        str = raw_input()
        send str to the server side
        fetch an element from the global queue
        deal with the element

如您所见,在接收线程中,我有一个 if 条件来测试服务器是否向客户端发送了“QUIT 信号”。如果有,那么我希望整个程序停止。

这里的问题是,在大部分时间里,发送线程被“raw_input()”阻塞并等待用户输入。当它被阻塞时,从另一个线程(接收线程)调用“sys.exit()”不会立即终止发送线程。发送线程必须等待用户输入内容并按下回车按钮。

有人能启发我如何解决这个问题吗?我不介意使用“raw_input()”的替代品。其实我什至不介意改变整个结构。

-------------编辑-------------

我在 linux 机器上运行它,我的 Python 版本是 2.7.5

【问题讨论】:

  • 你是用windows还是linux?
  • 应该得到一个“仁慈的简短代码”徽章。

标签: python multithreading


【解决方案1】:

你可以把发送线程设为daemonic:

send_thread = SendThread()  # Assuming this inherits from threading.Thread
send_thread.daemon = True  # This must be called before you call start()

如果只剩下运行的线程是守护进程,则不会阻止 Python 解释器退出。所以,如果剩下的唯一线程是send_thread,即使你在raw_input 上被阻塞,你的程序也会退出。

请注意,这将突然终止发送线程,无论它在做什么。如果它访问需要正确清理或不应中断的外部资源(例如写入文件),这可能会很危险。如果您正在做类似的事情,请使用threading.Lock 保护它,并且如果您可以获得相同的Lock,则仅从接收线程调用sys.exit()

【讨论】:

  • 它没有回答这个问题。它可能会解决问题,但它不会取消、中断、停止来自其他线程的输入:/
【解决方案2】:

简短的回答是你不能。 input() 就像很多这样的输入命令正在阻塞,并且它正在阻塞有关线程的所有内容是否已被杀死。您有时可以调用 sys.exit() 并让它根据操作系统工作,但它不会保持一致。有时您可以通过推迟到本地操作系统来终止程序。但是,那么您将不会广泛跨平台。

如果您有此功能,您可能需要考虑通过套接字汇集功能。因为与 input() 不同,我们可以很容易地做超时、线程和杀死事情。它还使您能够进行多个连接,并且可能接受更广泛的连接。

import socket
import time
from threading import Thread


def process(command, connection):
    print("Command Entered: %s" % command)
    # Any responses are written to connection.
    connection.send(bytes('>', 'utf-8'))


class ConsoleSocket:
    def __init__(self):
        self.keep_running_the_listening_thread = True
        self.data_buffer = ''
        Thread(target=self.tcp_listen_handle).start()

    def stop(self):
        self.keep_running_the_listening_thread = False

    def handle_tcp_connection_in_another_thread(self, connection, addr):
        def handle():
            while self.keep_running_the_listening_thread:
                try:
                    data_from_socket = connection.recv(1024)
                    if len(data_from_socket) != 0:
                        self.data_buffer += data_from_socket.decode('utf-8')
                    else:
                        break
                    while '\n' in self.data_buffer:
                        pos = self.data_buffer.find('\n')
                        command = self.data_buffer[0:pos].strip('\r')
                        self.data_buffer = self.data_buffer[pos + 1:]
                        process(command, connection)
                except socket.timeout:
                    continue
                except socket.error:
                    if connection is not None:
                        connection.close()
                    break
        Thread(target=handle).start()
        connection.send(bytes('>', 'utf-8'))

    def tcp_listen_handle(self, port=23, connects=5, timeout=2):
        """This is running in its own thread."""
        sock = socket.socket()
        sock.settimeout(timeout)
        sock.bind(('', port))
        sock.listen(connects)  # We accept more than one connection.
        while self.keep_running_the_listening_thread:
            connection = None
            try:
                connection, addr = sock.accept()
                address, port = addr
                if address != '127.0.0.1':  # Only permit localhost.
                    connection.close()
                    continue
                # makes a thread deals with that stuff. We only do listening.
                connection.settimeout(timeout)
                self.handle_tcp_connection_in_another_thread(connection, addr)
            except socket.timeout:
                pass
            except OSError:
                # Some other error.
                if connection is not None:
                    connection.close()
        sock.close()


c = ConsoleSocket()


def killsocket():
    time.sleep(20)
    c.stop()


Thread(target=killsocket).start()

这将为端口 23 (telnet) 上设置的连接启动一个侦听器线程,然后您进行连接并将该连接传递给另一个线程。它会启动一个 killsocket 线程,该线程会禁用各种线程并让它们平静地死去(出于演示目的)。但是,您不能在此代码中连接 localhost,因为您需要 input() 才能知道要向服务器发送什么,这会重现问题。

【讨论】:

    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2021-09-12
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多