Python:如何在其他线程中中断raw_input()

时间:2014-09-22 18:57:07

标签: python multithreading

我在python中编写一个简单的客户端服务器程序。在客户端程序中,我创建了两个线程(使用Python的线程模块),一个用于接收,一个用于发送。接收线程从服务器端连续接收字符串;而发送线程持续监听用户输入(使用raw_input())并将其发送到服务器端。这两个线程使用队列进行通信(本机同步,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 signal”。如果有,那么我希望整个程序停止。

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

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

------------- ------------- EDIT

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

2 个答案:

答案 0 :(得分:4)

你可以制作发送帖子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进行保护,并且只有在您可以获取相同sys.exit()时才从接收线程中调用Lock

答案 1 :(得分:1)

简短的答案是你不能。 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(远程登录)上设置的连接的侦听器线程,然后您进行连接,并将该连接传递给另一个线程。并且它启动了一个killsocket线程,该线程禁用了各个线程并让它们和平地死掉(用于演示目的)。但是,您无法在此代码内连接localhost,因为您需要input()知道要发送到服务器的内容,这会重新产生问题。