質問をすることでしか得られない、回答やアドバイスがある。

15分調べてもわからないことは、質問しよう!

新規登録して質問してみよう
ただいま回答率
85.37%
Python

Pythonは、コードの読みやすさが特徴的なプログラミング言語の1つです。 強い型付け、動的型付けに対応しており、後方互換性がないバージョン2系とバージョン3系が使用されています。 商用製品の開発にも無料で使用でき、OSだけでなく仮想環境にも対応。Unicodeによる文字列操作をサポートしているため、日本語処理も標準で可能です。

Q&A

解決済

2回答

39944閲覧

Pythonのthreadingにおける、終了時の処理

退会済みユーザー

退会済みユーザー

総合スコア0

Python

Pythonは、コードの読みやすさが特徴的なプログラミング言語の1つです。 強い型付け、動的型付けに対応しており、後方互換性がないバージョン2系とバージョン3系が使用されています。 商用製品の開発にも無料で使用でき、OSだけでなく仮想環境にも対応。Unicodeによる文字列操作をサポートしているため、日本語処理も標準で可能です。

0グッド

2クリップ

投稿2017/05/19 13:50

###前提・実現したいこと
Pythonを使って、動画や画像を表示するGUIを作っています。その中で、みなさまのご支援を頂ながらカメラの画像を取り込んで再生できるところまで来ました。

###発生している問題・エラーメッセージ
GUIのウインドウを閉じると、GUIを閉じることはできるのですが、threadがまだ残っているのかコマンドプロンプトのコントロールが帰ってきません。

###該当のソースコード

Python

1import wx 2import os 3import cv2 4import numpy as np 5import threading 6import sys 7 8GUI_IMAGE_X = 240 9 10GUI_IMAGE_Y = 180 11CAMMODE = False 12 13######################################################################## 14class DnDPanel(wx.Panel): 15 16 #---------------------------------------------------------------------- 17 def __init__(self, parent): 18 wx.Panel.__init__(self, parent=parent) 19 20 # Image box 21 img = wx.Image(GUI_IMAGE_X,GUI_IMAGE_Y) 22 23 # Statc bitmap 24 self.imageCtrl = wx.StaticBitmap(self, wx.ID_ANY,wx.Bitmap(img)) 25 26 # Text box 27 self.fileTextCtrl = wx.TextCtrl(self,style=wx.TE_MULTILINE|wx.HSCROLL|wx.TE_READONLY) 28 29 sizer = wx.BoxSizer(wx.HORIZONTAL) 30 sizer.Add(self.imageCtrl, 1, wx.ALL, 5) 31 sizer.Add(self.fileTextCtrl, 1, wx.EXPAND|wx.ALL, 5) 32 self.SetSizer(sizer) 33 self.Fit() 34 35 #---------------------------------------------------------------------- 36 def updateText(self, text): 37 #Overwtite a text 38 self.fileTextCtrl.SetValue(text) 39 40 #Add a text 41 #self.fileTextCtrl.WriteText(text) 42 43 #---------------------------------------------------------------------- 44 def updateImage_Entity(self, cv2image): 45 46 # Calculate aspect rat 47 mag_X = GUI_IMAGE_X / cv2image.shape[1] 48 mag_Y = GUI_IMAGE_Y / cv2image.shape[0] 49 50 if mag_X < mag_Y: 51 img = cv2.resize(cv2image, None, fx = mag_X, fy = mag_X) 52 else: 53 img = cv2.resize(cv2image, None, fx = mag_Y, fy = mag_Y) 54 55 # Calculate aspect ratio diff 56 # X-axis 57 diff_X = GUI_IMAGE_X - img.shape[1] 58 diff_X_start = round(diff_X/2) 59 diff_X_end = diff_X_start + img.shape[1] 60 61 # Y-axis 62 diff_Y = GUI_IMAGE_Y - img.shape[0] 63 diff_Y_start = round(diff_Y/2) 64 diff_Y_end = diff_Y_start + img.shape[0] 65 #print("[(%s,%s) : (%s,%s)]"%(diff_X_start,diff_Y_start,diff_X_end,diff_Y_end)) 66 67 # Create modified picture 68 bg_image = np.zeros((GUI_IMAGE_Y, GUI_IMAGE_X, 3), np.uint8) 69 70 bg_image[diff_Y_start:diff_Y_end,diff_X_start:diff_X_end] = img 71 # BGR --> RGB 72 img = cv2.cvtColor(bg_image, cv2.COLOR_BGR2RGB) 73 74 # Convert color array to bitmap image 75 img = wx.Bitmap.FromBuffer(img.shape[1], img.shape[0], img) 76 77 #Static bitmap 78 self.imageCtrl.SetBitmap(img) 79 80 81######################################################################## 82class CameraControl(threading.Thread): 83 84 def __init__(self,parent): 85 threading.Thread.__init__(self) 86 87 # Prepare camera mode 88 self.panelText = DnDPanel(parent).updateText 89 self.panelImage = DnDPanel(parent).updateImage_Entity 90 91 #---------------------------------------------------------------------- 92 def run(self): 93 94 global CAMMODE 95 if CAMMODE == False: 96 return False 97 98 # End other daemon thread at first 99 while threading.activeCount() > 2: 100 CAMMODE = False 101 102 # Start only one daemon thread 103 CAMMODE = True 104 105 while CAMMODE == True : 106 # Get camera information : "0" is device numer 107 cap = cv2.VideoCapture(0) 108 ret, frame = cap.read() 109 while ret == False: 110 # Get camera information : "0" is device numer 111 cap = cv2.VideoCapture(0) 112 ret, frame = cap.read() 113 self.panelText("ERROR: Camera is not working.") 114 return False 115 116 while ret == True: 117 cv2.waitKey(1) 118 ret, frame = cap.read() 119 self.panelText("Camera mode") 120 # Load an image to entity from file 121 self.panelImage(cv2.cvtColor(frame, cv2.COLOR_BGR2RGB)) 122 123 if CAMMODE == False: 124 ret = False 125 126######################################################################## 127class DnDFrame(wx.Frame): 128 #---------------------------------------------------------------------- 129 def __init__(self): 130 self.window = wx.Frame.__init__(self, parent=None, title="GUI test", style=wx.DEFAULT_FRAME_STYLE ^ wx.RESIZE_BORDER) 131 132 self.InitMenu() 133 self.panel = DnDPanel(self) 134 self.Fit() 135 136 self.cc = CameraControl(self) 137 self.cc.setDaemon(True) 138 self.cc.start() 139 140 self.Show() 141 142 #---------------------------------------------------------------------- 143 # Initialize 144 def InitMenu(self): 145 # Menubar 146 menubar = wx.MenuBar() 147 fileMenu = wx.Menu() 148 f_item2 = fileMenu.Append(wx.ID_ANY, '&Camera(C)', 'Load image from USB camera') 149 fileMenu.AppendSeparator() 150 f_item3 = fileMenu.Append(wx.ID_ANY, '&Quit(Q)', 'Quit application') 151 menubar.Append(fileMenu, '&File(F)') 152 153 self.SetMenuBar(menubar) 154 155 # Event call definition 156 self.Bind(wx.EVT_MENU, self.OnCamera, f_item2) 157 self.Bind(wx.EVT_MENU, self.OnQuit, f_item3) 158 159 # Frame definition 160 self.Centre() 161 162 # Status bar 163 self.CreateStatusBar() 164 self.setstatusbarTXT("") 165 166 self.Show(True) 167 168 #---------------------------------------------------------------------- 169 # Event hander: set status text 170 def setstatusbarTXT(self, msg): 171 self.SetStatusText(msg) 172 173 #---------------------------------------------------------------------- 174 # Event: Quit 175 def OnQuit(self,event): 176 global CAMMODE 177 CAMMODE = False 178 179 sys.exit() 180 181 #---------------------------------------------------------------------- 182 # Event: Camera 183 def OnCamera(self, event): 184 global CAMMODE 185 CAMMODE = True 186 self.panel.updateText("Camera mode") 187 self.setstatusbarTXT("Camera mode") 188 189 self.cc = CameraControl(self) 190 self.cc.setDaemon(True) 191 self.cc.start() 192 193#---------------------------------------------------------------------- 194if __name__ == "__main__": 195 app = wx.App(False) 196 frame = DnDFrame() 197 app.MainLoop() 198

###試したこと
class CameraControlのrunにあるwhileloopの中にprint("foobar")のようなでバグ文字列を吐かせましたが、GUI終了時にはデバグ文字列は出てきませんでしたので、whileloopで躓いているわけではなさそうです。

###補足情報
Win10(64bit)
Python 3.5.3(64bit)
wxPython 4.0.0a1 (Phoenix)

気になる質問をクリップする

クリップした質問は、後からいつでもMYページで確認できます。

またクリップした質問に回答があった際、通知やメールを受け取ることができます。

バッドをするには、ログインかつ

こちらの条件を満たす必要があります。

guest

回答2

0

ベストアンサー

昔に書いたコードなので詳細は覚えてないですが、どちらの方法でもmain終了時にthread側を終了できました。参考まで。

方法1

  • thread側でdaemon = Trueとして、main側の終了時にthread側も終了するようにする。

Python

1# -*- coding: utf-8 -*- 2import sys, time, datetime 3import threading 4 5# 排他制御付print 6def print_lock( ctx, str): 7 with ctx["lock"]: 8 print( str) 9 10# スレッド処理クラス 11class TestThread( threading.Thread): 12 13 def __init__(self, ctx): 14 super( TestThread, self).__init__() 15 self.daemon = True # メイン終了時にもスレッド終了する 16 self.ctx = ctx 17 18 def run( self): 19 print_lock( self.ctx, "sub loop start---") 20 while True: # 無限ループ 21 time.sleep(0.5) 22 print_lock( self.ctx, "sub : " + str(datetime.datetime.today())) 23 24 print_lock( self.ctx, "sub loop end---") 25 26if __name__ == '__main__': 27 # mainとスレッドで共有するデータ 28 ctx = {"lock":threading.Lock()} 29 30 th = TestThread( ctx) 31 th.start() 32 33 print_lock( ctx, "main loop start---") 34 for i in range(5): 35 time.sleep(1) 36 print_lock( ctx, "main : " + str(datetime.datetime.today())) 37 38 print_lock( ctx, "main loop end---")

方法2

  • main側から終了フラグctx["stop"]にてthread側の処理を終了できるようにする。
  • main側にて.join()にてthread側の処理を待つ。

Python

1# -*- coding: utf-8 -*- 2import sys, time, datetime 3import threading 4 5# 排他制御付print 6def print_lock( ctx, str): 7 with ctx["lock"]: 8 print( str) 9 10# スレッド処理クラス 11class TestThread( threading.Thread): 12 13 def __init__(self, ctx): 14 super( TestThread, self).__init__() 15 self.ctx = ctx 16 17 def run( self): 18 print_lock( self.ctx, "sub loop start---") 19 while True: # 無限ループ 20 if self.ctx["stop"]: # main側から終了を指示されたら終了 21 break 22 time.sleep(0.5) 23 print_lock( self.ctx, "sub : " + str(datetime.datetime.today())) 24 25 print_lock( self.ctx, "sub loop end---") 26 27if __name__ == '__main__': 28 # mainとスレッドで共有するデータ 29 ctx = {"lock":threading.Lock(), "stop":False} 30 31 th = TestThread( ctx) 32 th.start() 33 34 print_lock( ctx, "main loop start---") 35 for i in range(5): 36 time.sleep(1) 37 print_lock( ctx, "main : " + str(datetime.datetime.today())) 38 39 print_lock( ctx, "main loop end---") 40 ctx["stop"] = True # スレッド側に終了を指示 41 th.join() # スレッドの終了を待つ

投稿2017/05/19 14:43

can110

総合スコア38339

バッドをするには、ログインかつ

こちらの条件を満たす必要があります。

退会済みユーザー

退会済みユーザー

2017/05/20 03:47 編集

can110様 実際に動くコードを出して説明していただきありがとうございました。 私の書いたコードとcan110様のコードを混ぜ合わせながら動作確認をしたところ、無事動かすことが出来ました。 原因は、カメラの画像を無限ループで拾い続ける中で、sleepが挟まらないと処理が正常に終了できない、というところにありました。 ---(処理のイメージ)--- メインループ:wxPythonでGUI制御 子分スレッド:CameraControlでカメラ画像を無限ループで取り込み ※1 この無限ループの中でtime.sleep(0.5)がないと処理が終了できない ※2 私の環境ではsleep時間が、0.07では半々でクラッシュ、0.08なら時々クラッシュ、0.09でまれにクラッシュ、0.1でOKでした。 正常に動作させるために「わざとsleepを挟む」のが不思議な気がします。imshowのあとのwaitkey(1)も同じ挙動をしますよね…
can110

2017/05/20 04:16

pythonでのスレッドの詳細仕様は確認していないのですが、 一般的にはsleep(0.01)なりでもよいので呼び出すことによって、他のスレッドにも処理する余裕を与えます。 また、クラッシュする原因についてはソースを確認しておらず推測なのですが 同時にアクセスすべきではない(キャプチャ画像?)データに複数スレッドで同時アクセスしているためかもしれません。そのようなデータに対しては、一般的に排他(同期)アクセスする必要があります。 私のコードではthreading.Lock()オブジェクトで標準出力(print)へ排他アクセスをしています。
退会済みユーザー

退会済みユーザー

2017/05/20 07:51

can110様 詳しいご解説をありがとうございます。 問題がどこにあるかが見えてきましたので、排他アクセスとキャプチャ周りの挙動について、もう少し何が起きているかを調べたいと思います。
guest

0

Threadの終了処理の基本は以下の2パターンで、今回は後者を選択すると良いと思います。

python

1from threading import Thread 2import time 3import sys 4 5 6class MyThread(Thread): 7 des run(self): 8 time.sleep(9999) 9 10 11t = MyThread() 12t.setDaemon(True) # メインスレッドが終了すればこのスレッドも有無をいわさずに落とす 13t.start() 14sys.exit(0)

python

1from threading import Event 2from threading import Thread 3import time 4import sys 5 6 7class MyThread(Thread): 8 def __init__(self): 9 Thread.__init__(self) 10 self.stopping = Event() 11 12 def run(self): 13 while not self.stopping.is_set(): 14 time.sleep(1) 15 16 17t = MyThread() 18t.start() 19t.stopping.set() # 終了中フラグを立てる 20t.join() # スレッドが終了するまで待つ 21sys.exit(0)

投稿2017/05/20 13:18

編集2017/05/20 13:20
YouheiSakurai

総合スコア6142

バッドをするには、ログインかつ

こちらの条件を満たす必要があります。

退会済みユーザー

退会済みユーザー

2017/05/20 22:00

Youhei様 Event()を使ったフラグ管理のご回答をありがとうございます。 sleepの時間を1秒にすると1秒、5秒に変更すると5秒後に終了するのを確認しました。 後者のやり方はとてもシンプルで分かりやすい方法でした。
guest

あなたの回答

tips

太字

斜体

打ち消し線

見出し

引用テキストの挿入

コードの挿入

リンクの挿入

リストの挿入

番号リストの挿入

表の挿入

水平線の挿入

プレビュー

15分調べてもわからないことは
teratailで質問しよう!

ただいまの回答率
85.37%

質問をまとめることで
思考を整理して素早く解決

テンプレート機能で
簡単に質問をまとめる

質問する

関連した質問