簡體   English   中英

Python線程 - 管理線程終止和主線程

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

在你繼續閱讀之前,要知道我是Python新手並且對線程很新,所以請原諒我,如果我誤解了線程的工作方式或新手錯誤:P

我的目標的簡短描述:

  1. 主線程(a)做了一些事情(例如打印“開始!”)
  2. 主線程產生一個新線程(b)首先打印“線程B啟動”,然后永久打印x + 1(1,2,3 ......)
  3. 主線打印“Woop!”
  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))
    except
    KeyboardInterrupt: os._exit(1))
    except
    KeyboardInterrupt: os._exit(1))
    用戶代碼里面很好
  • 線程a 可以繼續運行而不是使線程b成為主線程,但在這種情況下,我不希望代碼處理Ctrl + C終止用戶代碼部分內的整個程序

這個例子不是我的實際目標,只是我遇到的問題的簡化版本。 我正在嘗試創建一個IRC框架,用戶可以在其中導入它並非常簡單地使用API​​,而不會使用線程和中斷等來清除自己的代碼。 這就是為什么用戶代碼盡可能干凈是很重要的。

該框架將允許用戶創建一個永遠運行的IRC機器人,在允許用戶添加自己的命令的同時監聽命令。 如果您感興趣的話,Github鏈接就在這里 (這是非常重要的WIP)。

在昨天的另一個問題上寫了一個關於類似問題的簡短說明,這是你可以在子線程“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 ,則表示該線程不存在或死亡,關閉子線程:)

你不能“切換”線程。 因此,一旦完成主線程,就必須等待其他線程使用方法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()

你無法像這樣切換線程。 它不像那樣工作。

但是,您可以使用帶有全局標志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后它會正常退出。

暫無
暫無

聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.

 
粵ICP備18138465號  © 2020-2024 STACKOOM.COM