0

在您继续阅读之前,请知道我是 Python 新手,并且对线程非常陌生,所以如果我误解了线程的工作原理或犯了新手错误,请原谅我:P

我的目标的简短描述:

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

这是我到目前为止所拥有的(基础知识):

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))
  • 线程 a可能会继续运行,而不是使线程 b 成为主线程,但在这种情况下,我不希望代码处理 Ctrl+C 终止用户代码部分内的整个程序

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

该框架将允许用户创建一个永久运行的 IRC 机器人,在听命令的同时允许用户添加自己的命令。如果您有兴趣,Github 链接在这里(这是非常 WIP atm)。

4

3 回答 3

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,它将指示线程不存在或已死,关闭您的子线程:)

于 2014-02-06T08:40:00.087 回答
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()
于 2014-02-06T09:24:11.403 回答
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 后优雅地退出。

于 2014-02-06T08:49:32.013 回答