简体   繁体   English

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

[英]Python Threading - Managing thread termination and the main thread

Before you read on, know that I'm new to Python and very new to threading so forgive me if I misunderstand how threads work or make a rookie error :P 在你继续阅读之前,要知道我是Python新手并且对线程很新,所以请原谅我,如果我误解了线程的工作方式或新手错误:P

Short description of my goal: 我的目标的简短描述:

  1. The main thread (a) does some things (eg prints "begin!") 主线程(a)做了一些事情(例如打印“开始!”)
  2. Main thread spawns a new thread (b) that first prints "Thread B started" and then prints x+1 forever (1, 2, 3 ...) 主线程产生一个新线程(b)首先打印“线程B启动”,然后永久打印x + 1(1,2,3 ......)
  3. Main thread prints "Woop!" 主线打印“Woop!”
  4. Then the end of the main thread is reached, it terminates itself and then switches to thread b making b the main thread 然后到达主线程的末尾,它终止自身然后切换到线程b使b成为主线程
  5. Program is now running thread b as the main thread so is just printing x+1 forever and a has been forgotten and is no longer relevant 程序现在正在运行线程b作为主线程,因此只是永远打印x + 1而且忘记了a并且不再相关
  6. Ctrl+C will terminate thread b and effectively, the whole program will be terminated because thread a doesn't exist anymore Ctrl + C将终止线程b并且有效地终止整个程序,因为线程a不再存在

Here's what I have so far (the basics): 这是我到目前为止(基础知识):

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!"

The requirements are: 要求是:

  • I don't want to modify below the 'User Code' mark much at all. 我根本不想在“用户代码”标记下面进行修改。 I certainly don't want to wrap it in a class, a function or it's own thread 我当然不希望将它包装在类,函数或它自己的线程中
  • Pressing Ctrl+C at any point should terminate the entire program with no threads left running (Using something like: except
    KeyboardInterrupt: os._exit(1))
    在任何时候按Ctrl + C都应该终止整个程序而不运行任何线程(使用类似: except
    KeyboardInterrupt: os._exit(1))
    except
    KeyboardInterrupt: os._exit(1))
    except
    KeyboardInterrupt: os._exit(1))
    inside User Code is fine
    except
    KeyboardInterrupt: os._exit(1))
    用户代码里面很好
  • Thread a may continue to run instead of making thread b the main thread, but in this case, I don't want the code to handle Ctrl+C terminating the entire program inside the User Code section 线程a 可以继续运行而不是使线程b成为主线程,但在这种情况下,我不希望代码处理Ctrl + C终止用户代码部分内的整个程序

This example is not my actual goal, just a simplified version of the problem I'm having. 这个例子不是我的实际目标,只是我遇到的问题的简化版本。 I'm trying to make an IRC framework where the user can import it and use the API very simply without messying their own code with threads and interrupts and the like. 我正在尝试创建一个IRC框架,用户可以在其中导入它并非常简单地使用API​​,而不会使用线程和中断等来清除自己的代码。 This is why it's important for the user code to be as clean as possible. 这就是为什么用户代码尽可能干净是很重要的。

The framework would allow the user to create an IRC bot which runs forever, listening to commands whilst allowing the user to add their own commands. 该框架将允许用户创建一个永远运行的IRC机器人,在允许用户添加自己的命令的同时监听命令。 The Github link is here if you're interested (It's very WIP atm). 如果您感兴趣的话,Github链接就在这里 (这是非常重要的WIP)。

Wrote a short note on another question yesterday having similar issues, this is a check you could implement in the subthread "b": 在昨天的另一个问题上写了一个关于类似问题的简短说明,这是你可以在子线程“b”中实现的检查:

Instead of while 1: do the following: 而不是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

It would be a good idea to store main in the global scope for all threads to use isntead of having to look up the main thread each and every initation of the sub-thread. main存储在全局范围内以供所有线程使用是一个好主意,而不必在每个子线程的初始化时查找主线程。 But this would do the work in your example. 但这可以在你的例子中完成工作。

main will be the handle towards your main thread by iterating through all threads ( .enumerate() ) and then placing the thread called "MainThread" into main and then calling main.isAlive() to check if it's still running. main将通过迭代所有线程( .enumerate() )然后将名为“MainThread”的线程放入main ,然后调用main.isAlive()来检查它是否仍在运行,从而处理主线程。 if main is None or False or if .isAlive() returns False it will indicate that the thread is either non-existant or dead, shutting down your subthread :) 如果mainNoneFalse或者.isAlive()返回False ,则表示该线程不存在或死亡,关闭子线程:)

You cannot "switch" threads. 你不能“切换”线程。 So once you're done with your main thread, you have to wait for the other threads to terminate using the method join . 因此,一旦完成主线程,就必须等待其他线程使用方法join终止。 But note this: 但请注意:

  • Since the join method is not interruptible with KeyboardInterrupt , you need to specify a timeout and loop over to detect user interrupts. 由于join方法是不是可中断的KeyboardInterrupt ,您需要通过指定超时和循环检测用户中断。
  • Since you can't force a thread to terminate, you have to implement a stop mecanism using threading.Event for instance 由于您无法强制终止线程,因此您必须使用threading.Event实现停止机制
  • You also need to use threading.Lock to prevent concurrent access on shared resources, like sys.stdout (used when you print) 您还需要使用threading.Lock来阻止对共享资源的并发访问,例如sys.stdout (打印时使用)

I gathered these aspects in a class called ThreadHandler , please have a look: 我在一个名为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()

You can't switch threads like that. 你无法像这样切换线程。 It doesn't work like that. 它不像那样工作。

However you could use signals with a global flag ALIVE : 但是,您可以使用带有全局标志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

Now it will gracefully quit after pressing CTRL+C. 现在按CTRL + C后它会正常退出。

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM