【问题标题】:(Python) Using threading to look for key input with getch(Python) 使用线程通过 getch 查找键输入
【发布时间】:2014-08-03 05:41:20
【问题描述】:

我一直在尝试编写一段测试代码,该代码将不断打印“正在运行”,直到按下某个键。我试图通过创建一个额外的线程(称为 thread1)来监听按键来实现这一点。

当我运行我的代码时,线程启动正常,并且在调用 getch.getch() 之前似乎可以正常执行。当 getch.getch() 正在等待按键时,它似乎不仅停止了 thread1,而且还停止了主线程。

如何确保在 thread1 监听按键时,主线程继续运行?

我正在使用 python 2.7 和 getch 1.0 (https://pypi.python.org/pypi/getch)。

这是我的代码:

import threading
import time
import getch

class myThread (threading.Thread):
    def __init__(self, threadID, name, cont):
        threading.Thread.__init__(self)
        self.threadID = threadID
        self.name = name
        self.cont = cont

    def run(self):
        print "Starting " + self.name +"\n"
        print "waiting 2 seconds"
        time.sleep(2)
        char = getch.getch()
        print 'You pressed %s' % char
        cont.append(1)
        print "Terminating" + self.name

cont = []

thread1 = myThread(1, "Thread1", cont)

thread1.start()

while cont == []:
    print "Running"
    time.sleep(0.5)

它输出这个:

Starting Thread1
Running

waiting 2 seconds
Running
Running
Running

它会一直呆在那里直到我按下一个键

【问题讨论】:

    标签: python multithreading io


    【解决方案1】:

    由于 GIL,您遇到了这个问题。如果您将threading.Thread 设为multiprocessing.Process,则效果很好:

    class myThread (multiprocessing.Process):
        def __init__(self, threadID, name, cont):
            super(myThread, self).__init__()
            #threading.Thread.__init__(self)
            self.threadID = threadID
            self.name = name
            self.cont = contdef run(self):
            print "Starting " + self.name +"\n"
            char = getch.getch()
            print 'You pressed %s' % char
            cont.append(1)
            print "Terminating" + self.name
    
    cont = []
    
    thread1 = myThread(1, "Thread1", cont)
    
    thread1.start()
    
    while cont == []: 
        print "Running"
        time.sleep(0.5)
    

    输出:

    dan@dantop:~$ ./get.py 
    Running
    Starting Thread1
    
    Running
    Running
    Running
    Running
    Running
    Running
    Running
    Running
    You pressed f
    TerminatingThread1
    Running
    Running
    Running
    Running
    Running
    Running
    Running
    Running
    

    getch 是一个 C 扩展,正在对 getchar() 进行阻塞调用,但它不会首先释放 GIL。因为 Python 实际上不能同时运行两个线程,所以它卡在等待阻塞调用 getchar() 的工作线程中。

    您实际上可以很容易地修复此错误,方法是使用Py_BEGIN_THREADS 和Py_ALLOW_THREADS 从getch C 扩展代码中显式释放 GIL:

    static PyObject *getch_getche(PyObject *self, PyObject *args)
    {
        int ok = PyArg_ParseTuple(args, "");
        char c;
        Py_BEGIN_ALLOW_THREADS
        c = getche();
        Py_END_ALLOW_THREADS
        return PyUnicode_FromFormat("%c", c); 
    }
    
    static PyObject *getch_getch(PyObject *self, PyObject *args)
    {
        int ok = PyArg_ParseTuple(args, "");
        char c;
        Py_BEGIN_ALLOW_THREADS
        c = getch();
        Py_END_ALLOW_THREADS
        return PyUnicode_FromFormat("%c", c); 
    }
    

    如果您对 getchmodule.c 进行更改并重新构建扩展,则原始的、使用线程的示例代码可以正常工作。

    【讨论】:

    • 它可能会碰到 gil,因为 getch 是一个阻塞的系统级调用而不是一个阻塞的 python 调用...... raw_input 是一个阻塞的 python 调用(但它是一个 python 级调用)并且工作正常,但我认为从操作系统级别而不是 python 获取块
    • 完美!那成功了。虽然它没有打破循环,但这是一个单独的问题。谢谢!
    • @JoranBeasley 是的,没错。它在不释放 GIL 的情况下阻塞 C 扩展。我更新了我的答案以解释这一点以及如果需要如何解决它。
    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 2015-02-08
    • 2014-04-25
    • 2021-11-01
    • 1970-01-01
    • 2018-10-27
    • 2014-09-11
    • 1970-01-01
    相关资源
    最近更新 更多