问题描述
我一直试图写一段测试代码,它会不断打印Running直到按下一个键。我试图通过创建一个监听按键的附加线程(称为thread1)来实现这一点。
当我运行我的代码时,线程启动正常,并且似乎执行正常,直到调用getch.getch()。虽然getch.getch()正在等待按键,但它似乎不仅停止了thread1,而且还停止了主线程。
我如何确保在thread1侦听按键时主线程继续运行?
我'使用python 2.7和getch 1.0(这是我的代码:
import threading
导入时间
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开始+ self.name +\ n
打印等待2秒
time.sleep(2)
char = getch.getch()
打印'你按%s'%char
cont.append(1)
打印终止+ self.name
cont = []
$ b thread1 = myThread(1,Thread1,续)
thread1.start()
而cont == []:
printRunning
time.sleep(0.5)
它输出:
开始Thread1
运行
等待2秒
运行
运行
运行
它一直待到我按下一个键
你正在打这个问题是因为GIL。如果您使 threading.Thread
a multiprocessing.Process
:
$,它可以正常工作b $ b
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):
printStarting+ self.name +\ n
char = getch.getch()
print'你按%s'%char
cont.append(1)
打印终止+ self.name
cont = []
thread1 = myThread(1,Thread1,续)
thread1.start()
而cont == []:
printRunning
time.sleep(0.5)
输出:
dan @ dantop:〜 $ ./get.py
运行
开始Thread1
运行
运行
运行
运行
运行
运行
运行
运行
你按下f
终止线程1
运行
运行
运行
运行
运行
运行
运行
运行
getch
是一个C-extension,正在阻止调用 getchar()
,但它不会首先释放GIL 。因为Python实际上不能同时运行两个线程,所以它会卡在工作线程中,等待阻塞对 getchar()
的调用。
通过使用 Py_BEGIN_THREADS $ c从
getch
C扩展代码显式释放GIL,您可以非常轻松地修复此错误$ c>和 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
返回PyUnicode_FromFormat(%c,c);
}
静态PyObject * getch_getch(PyObject * self,PyObject * args)
{
int ok = PyArg_ParseTuple(args,);
char c;
Py_BEGIN_ALLOW_THREADS
c = getch();
Py_END_ALLOW_THREADS
返回PyUnicode_FromFormat(%c,c);
}
如果您将更改改为 getchmodule.c
并重建扩展,原始的,使用线程的示例代码工作正常。
I've been trying to write a piece of test code which will continually print "Running" until a key is pressed. I've tried to implement this by creating an additional thread (called thread1) that will listen for a keypress.
When I run my code, the thread launches fine, and seems to execute properly until getch.getch() is called. While getch.getch() is waiting for a keypress, it seems to stop not only thread1, but also the main thread.
How can I ensure that while thread1 listens for a keypress, the main thread keeps running?
I'm using python 2.7 and getch 1.0 (https://pypi.python.org/pypi/getch).
Here's my code:
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)
It outputs this:
Starting Thread1
Running
waiting 2 seconds
Running
Running
Running
And it stays there until I press a key
You're hitting this issue because of the GIL. It works fine if you make the threading.Thread
a 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)
Output:
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
is a C-extension, and is doing a blocking call to getchar()
, but it doesn't release the GIL first. Because Python cannot actually run two threads concurrently, it gets stuck in the worker thread waiting for blocking call to getchar()
.
You can actually fix this bug pretty easily, by explicitly releasing the GIL from the getch
C-extension code, using Py_BEGIN_THREADS
and 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);
}
If you make that change to getchmodule.c
and rebuild the extension, the original, thread-using, example code works fine.
这篇关于(Python)使用线程通过getch查找键输入的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持!