如何停止QThread "gracefully"?

How to stop QThread "gracefully"?

我得到了一些帮助来完成以下代码。但是我需要从 Worker 线程中的这个循环中跳出,所以当我退出应用程序时,它不会崩溃。目前,当我退出应用程序时,QThread 仍然是 运行。 如果我使用 break 语句,它会起作用,但我无法再搜索主机,因为循环已完全关闭。我尝试了几种方法来做到这一点,但没有运气。我是编程新手。

class Worker(QThread):
    found = Signal(str)
    notFound = Signal(str)
    def __init__(self):
        QThread.__init__(self)
        self.queue = Queue()

    def run(self):
        while True:
            hostname = self.queue.get()
            output_text = collect_host_status(hostname)
            for i in output_text:
                if "not found" in i:
                    self.notFound.emit(i.replace(" not found", ""))
                else:
                    self.found.emit(i)

    def lookUp(self, hostname):
        self.queue.put(hostname)


class MainWindow(QMainWindow):
    def __init__(self):
        # ...
        self.ui.pushButton_2.clicked.connect(self.buttonclicked)

        self.thread = Worker()
        self.thread.found.connect(self.ui.textEdit_2.append)
        self.thread.notFound.connect(self.ui.textEdit_3.append)
        self.thread.start()

    def buttonclicked(self):
        if self.ui.textEdit.toPlainText():
            self.thread.lookUp(self.ui.textEdit.toPlainText())

收集主机状态的代码如下:

def get_brss_host_status(host):
    
    host = host.lower()
    api_url = 'https://some.system.with/api/'
    host_search = 'status/host/{}?format=json'.format(host)

    
    r = requests.get(api_url + host_search, auth=(loc_brss_user, loc_brss_passwd))
    request_output = r.text
    if request_output == '{"error":"Not Found","full_error":"Not Found"}':
        host2 = host.upper()
        host_search2 = 'status/host/{}?format=json'.format(host2)
        r2 = requests.get(api_url + host_search2, auth=(loc_brss_user, loc_brss_passwd))
        request_output2 = r2.text
        # print('Debug request_output2', request_output2)
        if request_output and request_output2 == '{"error":"Not Found","full_error":"Not Found"}':
            output_string = host + " not found"
        else:
            output_string = host2

    else:
        output_string = host
    
    return output_string


def collect_host_status(hosts):
    
    hosts_list = list(hosts.split("\n"))
        
    status_list = []
    for i in hosts_list:
        
        host = get_brss_host_status(i)
        status_list.append(host)
    
    return status_list

正如@ekhumoro 在评论中所建议的那样,基本解决方案是在 while 循环中使用一个简单的标志,这将确保一旦循环重新开始,如果不遵守条件,它就会退出。

不过,应注意两个重要方面:

  • 使用队列的基本get()使循环无限等待;
  • 如果出现任何网络问题(临时网络问题等),示例中的功能(网络请求)可能会延迟一段时间;

要正确解决这些问题,应进行以下修改:

  • get() 应该使用超时,这样即使没有请求在排队,它也允许退出循环;作为替代方案,您可以取消设置“运行”标志,将任何内容添加到队列并在继续之前检查标志:这确保您不必等待队列 get() 超时;
  • 网络请求也应该有一个最小超时时间;
  • 它们应该从线程单独完成,而不是分组,这样如果请求的主机列表太大并且您想在查找时退出,线程可以退出;
from queue import Queue, Empty

class Worker(QThread):
    found = Signal(str)
    notFound = Signal(str)
    def __init__(self):
        QThread.__init__(self)
        self.queue = Queue()

    def run(self):
        self.keepRunning = True
        while self.keepRunning:
            hostList = self.queue.get()
            if not self.keepRunning:
                break

            # otherwise:
            # try:
            #     hostList = self.queue.get(timeout=1)
            # except Empty:
            #     continue

            for hostname in hostList.splitlines():
                if not self.keepRunning:
                    break
                if hostname:
                    output_text = get_brss_host_status(hostname)
                    if output_text is None:
                        continue
                    if "not found" in output_text:
                        self.notFound.emit(output_text.replace(" not found", ""))
                    else:
                        self.found.emit(output_text)

    def stop(self):
        self.keepRunning = False
        self.queue.put(None)

    def lookUp(self, hostname):
        self.queue.put(hostname)

并在 get_brss_host_status 中更改以下内容:

def get_brss_host_status(host):
    
    host = host.lower()
    api_url = 'https://some.system.with/api/'
    host_search = 'status/host/{}?format=json'.format(host)


    try:
        r = requests.get(api_url + host_search, 
            auth=(loc_brss_user, loc_brss_passwd), 
            <b>timeout=1</b>)
    except Timeout:
        return

    # ...