用了有一段时间Python, 遇到的第一个比较烦的问题, blocking raw_input

我有一个daemon程序, 运行中要求能接收用户输入命令退出, 也能超时退出

这两个事情都很简单, 前者:
daemon.start() # daemon在另一线程中运行
While True:
    cmd = raw_input()
    if cmd == ‘exit’:
        break
daemon.stop()

后者更简单了
daemon.start()
sleep(10)
daemon.stop

两个一起做
daemon.start()
While True:
    cmd = raw_input(1) # 检测一秒的input, 到时就退出
    if datetime.now() > exit_time:
        break
    elif cmd == ‘exit’:
        break
daemon.stop()

可是问题来了, raw_input不支持timeout, 它只能一直等到用户有输入为止, 彻底的blocking操作

搜索了一下发现不少人有这个困扰, 比如这个:
http://www.gossamer-threads.com/lists/python/dev/791714
在*nix下很简单, 用select等到stdin有输入为止, 而select是可以timeout的, 可是Windows下的select只能在socket上工作, stdin不行

类似的还有个用signal的解决方法, 不过兼容性就更差了

这里有个办法在Windows上调用msvcrt来实现non blocking input
http://bytes.com/topic/python/answers/46473-non-blocking-raw_input
可是看起来就很丑…

换条路, 分两个线程做, 一个线程里面sleep等超时, 另一个线程里面raw_input, 超时了就把raw_input线程杀掉, 可是python里面不能随便杀掉线程…

后来因为时间紧迫, 用了一个很丑的方案: 超时之后, 用ctypes直接调用TerminateProcess结束进程, 也就杀死了raw_input, 反正当时只在Windows下运行

可是最近要移植Linux, TerminateProcess肯定是不能用了, 试了一下用sys.exit()退不掉, 试验了一下是因为我在主线程里raw_input, 另一线程里超时sys.exit(), 这在Windows下ok, 在Linux下sys.exit()只会退掉超时那个线程, 影响不到主线程, 如果反过来在另一线程里raw_input, 在主线程里超时sys.exit(), Linux下就能正常结束掉

总之是要改, 还是想改得不那么丑陋, 找来找去突然发现threading.Thread有一个daemon属性:
http://docs.python.org/library/threading.html#threading.Thread.daemon
The entire Python program exits when no alive non-daemon threads are left.
这话说得真够绕的…

这下清爽了, 不用任何平台相关的花招就可以搞定:
from time import sleep
from threading import Thread, Event

e = Event()

def read_loop():
        while True:
                print ‘type something:’,
                a = raw_input().strip()
                if a == ‘exit’:
                        e.set() # 设置Event让主线程退出
                        break
                else:
                        print ‘you typed:’ + a

t = Thread(target = read_loop)
t.daemon = True # 关键, 不设这个的话超时之后read_loop线程还在, 进程退不掉
t.start()

e.wait(10)

if e.is_set():
        print ‘you typed exit’
else:
        print ‘time out’

Advertisements

1 comment so far

  1. JimmyZ on

    一直在用python raw_input blocking搜索, 无外乎文中所引用sys.exit()/TerminateProcess/select/msvcrt.kbhit几种方式

    等找到daemon这方法之后, 想起来再用python raw_input blocking daemon搜索, 发现其实stackoverflow上早就有人这么说了:
    http://stackoverflow.com/questions/552996/python-exit-a-blocking-thread
    可是这个三楼没有得到注意… 而那个最佳答案根本不能解决问题, 它跟我文中最后一个范例中主线程退出的方法类似, 可是raw_input完全blocking根本没有检查Event/超时这样的机会呀…


发表评论

Fill in your details below or click an icon to log in:

WordPress.com Logo

You are commenting using your WordPress.com account. Log Out / 更改 )

Twitter picture

You are commenting using your Twitter account. Log Out / 更改 )

Facebook photo

You are commenting using your Facebook account. Log Out / 更改 )

Google+ photo

You are commenting using your Google+ account. Log Out / 更改 )

Connecting to %s

%d 博主赞过: