Python线程 - 管理线程终止和主线程

时间:2014-02-06 08:26:34

标签: python multithreading concurrency keyboardinterrupt

在你继续阅读之前,要知道我是Python新手并且对线程很新,所以请原谅我,如果我误解了线程是如何工作的或者是新手错误:P

对我目标的简短描述:

  1. 主线程(a)做了一些事情(例如打印“开始!”)
  2. 主线程产生一个新线程(b),首先打印“线程B” 开始“然后永远打印x + 1(1,2,3 ......)
  3. 主线打印“Woop!”
  4. 然后到达主线程的末尾,它终止并且 然后切换到线程b使b成为主线程
  5. 程序现在正在运行线程b作为主线程,所以只是 永远打印x + 1并且已经忘记并且不再存在 相关
  6. Ctrl + C将终止线程b并有效地终止整个程序 将被终止,因为线程a不再存在
  7. 这是我到目前为止(基础知识):

    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. 终止用户代码部分内的整个程序

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

    该框架允许用户创建一个永远运行的IRC机器人,在允许用户添加自己的命令的同时监听命令。如果您感兴趣的话,Github链接是here(这是非常重要的WIP)。

3 个答案:

答案 0 :(得分: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 :(得分:1)

你不能“切换”线程。因此,一旦完成主线程,就必须等待其他线程使用方法join终止。但请注意:

  • 由于join方法不能用KeyboardInterrupt中断,因此您需要指定超时并循环以检测用户中断。
  • 由于您无法强制线程终止,因此您必须使用threading.Event实现停止机制,例如
  • 您还需要使用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()

答案 2 :(得分: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后它会正常退出。