(Python)使用线程通过getch监测键盘输入
我一直在尝试写一段测试代码,这段代码会不停地打印“正在运行”,直到我按下一个键。我试着通过创建一个额外的线程(叫做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
而且它会一直停在那里,直到我按下一个键
1 个回答
4
你遇到这个问题是因为有个叫做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()
的阻塞调用结束。
其实你可以很简单地修复这个问题,只需要在getch
的C扩展代码中明确释放GIL,使用Py_BEGIN_THREADS
和Py_ALLOW_THREADS
:
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
做了这个修改并重新编译扩展,原来的使用线程的示例代码就能正常工作了。