0
0

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?

pythonでやってしまってハマったこと②

Posted at

Threadの隠れメソッドの話

pythonでは、Threadクラスを継承して独自のスレッドを実装することができます。
そこで、汎用的に使用できるワーカースレッドのベースクラスを作成しました。

内容は大体、以下のような感じです。

Worker.py
import time
from queue import Queue
from threading import Event, Thread
from abc import abstractmethod

class Worker(Thread):
    def __init__(self, pipe_name, exit_msg='exit'):
        super().__init__()
        self._stop = Event()
        self._queue = Queue()
        # ここに名前付きパイプの初期化処理
        self._pipe = PipeWatch(pipe_name, exit_msg=exit_msg, self._queue, self._stop)
        # PipeWatchは名前付きパイプを非同期に読みexit_msgが来たら_stop.set()します
        # それ以外は、_queueに追加して、本クラスのサブクラスで読み込ませる
    
    @abstractmethod
    def onStart(self):
        pass# サブクラスはオーバーライドしてFalseを返せばスレッドは終わる
    @abstractmethod
    def loop(self):
        pass# サブクラスはオーバーライドしてFalseを返せばスレッドは終わる
    @abstractmethod
    def onException(self, ex):
        pass# 例外はサブクラスのオーバーライドで処理させる
    @abstractmethod
    def onClosing(self):
        pass# 閉塞はサブクラスのオーバーライドで処理させる
    def dequeue(self):
        _data = None
        if not self._queue.empty():
            _data = self._queue()
        return _data
    def enqueue(self, data):
        if self._queue.put(data)
    def start(self):
        super().start()
        self._pipe.start() 
    def run(self):
        try:
            if self.onStart() != False:
                while not self._stop.is_set():
                    if self.loop() == False:
                        break
                    time.sleep(0.1)
        except BaseException as ex:
            self.onException(ex)
        finally:
            self.onClosing()
            self._pipe.stop()
            

このクラスの大体の仕様は、

  • スレッドは、異なる.pyファイルに書かれていても、通信が行えるイメージ
  • スレッド間の通信には、ソケットは使わず、名前付きパイプを使用する
  • 両方が開かれてないとブロックされる名前付きパイプではなく、非ブロッキングモードでオープンし、必要なときに操作を取消できる

このクラスのサブクラスを実装して動かしたところ、期待した動作をするようになりましたが、スレッド終了時に、終了せずにいつまでも終わらないという問題が発生しました。
そして、よく見ると、以下のようなエラーメッセージが出ています。

Terminal
File "/usr/lib/python3.11/threading.py", line 1134, in _wait_for_tstate_lock
self._stop()
		TypeError: 'Event' object is not callable

最初は訳が分かりませんでした。
でも、/usr/lib/python3.11/threading.py を開いてみて原因が分かりました。
pythonのスレッドモジュールには、ドキュメントには書かれていない、
Thread._stop() というメソッドが存在していたのです。

私のコードには、以下のような記述があります。

Worker.py 抜粋
class Worker(Thread):
    def __init__(self, pipe_name, exit_msg='exit'):
        super().__init__()
        self._stop = Event()    # <-これ!これが原因です。
        self._queue = Queue()

pythonのthreadingモジュールはスレッドを停止させるときに、
Thread._stop() を呼んでいたのです。
でも、そこは、私がイベントとして_stop を使ってしまっていたのです。
だから、

Terminal
		TypeError: 'Event' object is not callable

と怒られていたのです。

0
0
0

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
0
0

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?