2014-02-06 102 views
0

在您阅读上,要知道我是新来的Python和非常新的线程,因此原谅我,如果我误会线程如何工作或做一个菜鸟错误:PPython的线程 - 管理线程终止与主线程

我的目标

简短说明:

  1. 主线程(一)确实有些东西(如打印!“开始”)
  2. 主线程产生一个新的线程(b)在第一次印“线程b 开始“然后永远打印x + 1(1,2,3 ...)
  3. 主线打印“Woop!”
  4. 然后在达到主线程结束,则终止本身和 然后切换到线程B使B中的主线程
  5. 方案现在正在运行的线程B作为主线程所以仅仅是 印刷X + 1永远和一个已经被人遗忘,不再 相关
  6. 按Ctrl + C将终止线程b,有效,整个程序 将被终止,因为线程不存在了

这里就是我有这么(基本):

import threading, time 

def printCount(): 
    print "Thread B started" 
    x = 0 
    while True: 
     time.sleep(1) 
     x = x + 1 
     print x 

## User Code ## 
print "begin!" 
threadB = threading.Thread(target=printCount) 
threadB.start() 
print "woop!" 

的要求是:

  • 我不想太多的所有修改“用户守则”关口下方。我 当然不希望它包装在一个类,函数或它自己的 螺纹
  • 按Ctrl + C在任何时候应该终止整个程序 没有线程保持运行(使用类似:except
    KeyboardInterrupt: os._exit(1))
    内用户代码是罚款
  • 一个线程可以继续运行,而不是让线程b的主要 线程,但在这种情况下,我不想代码来处理按Ctrl + C 终止用户代码中的整个程序部分

This例子不是我的实际目标,只是我遇到的问题的简化版本。我正在尝试构建一个IRC框架,用户可以在其中导入它并非常简单地使用API​​,而无需使用线程和中断等来混淆他们自己的代码。这就是为什么使用户代码尽可能干净至关重要。

该框架将允许用户创建一个永久运行的IRC bot,监听命令,同时允许用户添加自己的命令。如果你感兴趣的话,Github链接是here(这是非常WIP atm)。

回答

1

你不能“切换”线程。所以一旦你完成了你的主线程,你必须等待其他线程终止使用方法join。但要注意的是:

  • 由于join方法不是可中断的KeyboardInterrupt,您需要通过指定超时和循环检测用户中断。
  • 既然你不能强迫一个线程终止,你必须实现使用threading.Event例如停止mecanism
  • 你还需要使用threading.Lock,以防止对共享资源并发访问,如sys.stdout(用来当您打印)

我收集这些方面在一个名为ThreadHandler类,请看看:

import threading, time 

def printCount(lock, stop): 
    with lock: 
     print "Thread B started" 
    x = 0 
    while not stop.is_set(): 
     time.sleep(1) 
     x = x + 1 
     with lock: 
      print x 

class ThreadHandler(): 
    STEP = 0.2 

    def __init__(self, target): 
     self.lock = threading.Lock() 
     self.stop = threading.Event() 
     args = (self.lock, self.stop) 
     self.thread = threading.Thread(target=target, args=args) 

    def start(self): 
     self.thread.start() 

    def join(self): 
     while self.thread.is_alive(): 
      try: 
       self.thread.join(self.STEP) 
      except KeyboardInterrupt: 
       self.stop.set() 

## User Code ## 
print "begin!" 
handler = ThreadHandler(target=printCount) 
handler.start() 
with handler.lock: 
    print "woop!" 
handler.join() 
1

昨天写一张纸条上的另一个问题具有类似的问题,这是一个检查,你可以在子线程“b”的实施:

代替while 1:做到以下几点:

def printCount(): 
    main = None 
    for t in threading.enumerate(): 
     if t.name == 'MainThread': 
      main = t 
    print "Thread B started" 
    x = 0 
    while main and main.isAlive(): 
     time.sleep(1) 
     x = x + 1 
     print x 

会在全局范围内存储main是一个不错的主意,因为所有线程都可以使用,而不必每次启动子线程时都要查看主线程。 但是这会做你的例子中的工作。

main将朝着你的主线程手柄通过所有线程(.enumerate()),然后将名为“MainThread”线程进入main迭代,然后调用main.isAlive()检查它是否仍在运行。 如果mainNoneFalse或者.isAlive()回报False将表明线程要么是不存在的或死的,关闭你的子线程:)

+1

我想包含printCount继续永远运行线程,但这间esting! – Jazcash

+0

@Jazcash哦,我的坏,我以为我读到你想要的线程不会永远运行:) – Torxed

0

不能切换这样的线程。它不这样工作。

但是你可以使用信号与全局标志ALIVE

import threading, time, signal 

ALIVE = True 

def handle_sigint(signum, frame): 
    global ALIVE 
    ALIVE = False 

signal.signal(signal.SIGINT, handle_sigint) 

def printCount(): 
    print "Thread B started" 
    x = 0 
    while ALIVE: # <--- note the change 
     time.sleep(1) 
     x = x + 1 
     print x 

## User Code ## 
print "begin!" 
threadB = threading.Thread(target=printCount) 
threadB.start() 
print "woop!" 

signal.pause() # <--- wait for signals 

现在它将会优雅地按下CTRL + C后退出。

+0

这很不错,但你有用户代码中的signal.pause()。也许我应该创造一个更好的例子。主线程结束后是否有办法启动线程? – Jazcash

+1

@Jazcash你可以从任何线程启动线程。但是,您需要主线程来处理像CTRL + C这样的信号。如果主线程终止,那么你不会捕获该信号。 – freakish