回答編集履歴

3

実際のアプリケーションでの事例を追記

2021/08/25 10:42

投稿

teamikl
teamikl

スコア8681

test CHANGED
@@ -257,3 +257,23 @@
257
257
  組み込み環境で消費電力が気になる、
258
258
 
259
259
  等といった事情でもない限り優先度は低めになると思います。
260
+
261
+
262
+
263
+
264
+
265
+
266
+
267
+ ----
268
+
269
+ 追記2: 実際のアプリケーションでの after を用いた queue の読み出し
270
+
271
+
272
+
273
+ - Tkinter製IDE: IDLE のコード (idlelib.rpc) pollresponse 内で queue.get
274
+
275
+  idlelib.pyshell poll_subprojecc内でafter
276
+
277
+ - Tkinter製IDE: Thonny のコード (thonny.workbench)
278
+
279
+   [_poll_ipc_requests](https://github.com/thonny/thonny/blob/6f429da861b47d1a72d75c64978fc65a26b3ecd4/thonny/workbench.py#L2235)

2

コードのフォーマット修正

2021/08/25 10:41

投稿

teamikl
teamikl

スコア8681

test CHANGED
@@ -176,13 +176,17 @@
176
176
 
177
177
  対策する場合、一度のafterで読み出せる回数に制限を設けます。例)
178
178
 
179
+ ```python
180
+
179
181
  for _ in range(5):
180
182
 
181
-  if queue.empty()
183
+ if queue.empty()
182
-
184
+
183
-   break
185
+ break
184
-
186
+
185
-  ...
187
+ ...
188
+
189
+ ```
186
190
 
187
191
 
188
192
 

1

queue.get の場合を追記。queue.put とトピックを分離

2021/08/25 03:34

投稿

teamikl
teamikl

スコア8681

test CHANGED
@@ -1,7 +1,15 @@
1
+ # queue.put に関して
2
+
3
+
4
+
1
5
  > サブスレッド->メインスレッドの場合はafter()を使えばいいのでしょうか?
2
6
 
3
7
 
4
8
 
9
+ 追記: queue.get での after に関しては後述
10
+
11
+
12
+
5
13
  after, after_idle で、「対象の関数をメインスレッド側で呼び出させること」はできます。
6
14
 
7
15
  大抵の場合、サブスレッド→メインスレッドの queue の代替として使えます。
@@ -85,3 +93,163 @@
85
93
  例えば、tcl のライブラリを含まずにアプリケーションを配布を想定する場合に、
86
94
 
87
95
  問題となる可能性があります。
96
+
97
+
98
+
99
+ ----
100
+
101
+
102
+
103
+ # queue.get に関して
104
+
105
+
106
+
107
+
108
+
109
+
110
+
111
+
112
+
113
+ > サブスレッドでメッセージをput、メインスレッドでgetする。
114
+
115
+ このキューはメインスレッドでafterを常に回すことでgetする、という方法自体(質問文のコード)は問題ないでしょうか。
116
+
117
+
118
+
119
+ 頻度次第です。
120
+
121
+
122
+
123
+ - OK: queue.get() ではブロッキングの可能性がある為、get_nowait を使う。
124
+
125
+
126
+
127
+ - afterの呼び出し間隔次第でラグが問題にならない様なら大丈夫です。
128
+
129
+ - 間隔が短い(高頻度に呼び出される)事による負荷が、
130
+
131
+  実行環境次第で問題になる事はあるかもしれません。
132
+
133
+
134
+
135
+  100ms 程度(10回/1秒) であれば問題ないと思いますが、
136
+
137
+  ユーザーがラグと認識しない程度に増やしても大丈夫です。
138
+
139
+  (過剰なケース: 1ms 間隔で呼び出し → 不要な呼び出しが大量発生)
140
+
141
+
142
+
143
+
144
+
145
+ 懸念事項:
146
+
147
+ - empty() を確認してから get 迄に別の場所で読み取られていた場合、エラーになる。
148
+
149
+  現状のコードではマルチスレッドを考慮しなくても良い為、問題なしですが、
150
+
151
+  empty は確認せずに、get 時の例外で queue.Empty を捕捉する方が汎用的です。
152
+
153
+
154
+
155
+ > while not from_sub_to_main.empty():
156
+
157
+
158
+
159
+ がキューへのputが読み出し速度を上まわる場合、
160
+
161
+ 実質無限ループとなり、GUIが応答なしとなる原因に成り得ます。
162
+
163
+
164
+
165
+ 質問のコードの頻度の queue.put では問題ありませんが、
166
+
167
+ 例えば、動画のフレームをqueueでやる取する場合は調整が必要です。
168
+
169
+
170
+
171
+ ライブラリ化して、汎用的なコードにしたい場合は対策が必要。
172
+
173
+ 現状のコードでしか使わない実装なら対策不要。
174
+
175
+
176
+
177
+ 対策する場合、一度のafterで読み出せる回数に制限を設けます。例)
178
+
179
+ for _ in range(5):
180
+
181
+  if queue.empty()
182
+
183
+   break
184
+
185
+  ...
186
+
187
+
188
+
189
+ ---
190
+
191
+ after 以外でキューの読み取り手段もありますが、
192
+
193
+ お勧めしにくい理由として、サンプルコードをほぼ見かけたことありません。
194
+
195
+ 出典を出せればよいのですが…
196
+
197
+
198
+
199
+ - A: サブスレッドで event_generate を使い通知、
200
+
201
+  メイン側で bind した任意の関数を呼び出す。
202
+
203
+  ※ event_generate は thread-safe な関数
204
+
205
+ - B: 非同期IO (asyncio) で動かす。
206
+
207
+  利点は、Event 等、queue 以外の排他制御の手段が取れる。
208
+
209
+  但し、プログラム全体を asyncio に対応させる必要あり。
210
+
211
+ - C: createfilehandler を用いた通知
212
+
213
+
214
+
215
+ 解説
216
+
217
+ after での実行のように、
218
+
219
+ ループで毎回データが届いているか確認する方式を polling といい、
220
+
221
+ こういった方式での実装に共通した、問題点・改善方法が知られています。
222
+
223
+ 上に示した方法は、何れもこの問題を改良・効率化する方法です。
224
+
225
+
226
+
227
+ after 100ms では、約10回/1秒 程度の頻度での呼び出しですが、
228
+
229
+ 動画再生等の高頻度のやり取りでもない限り、大半が不要な呼び出しとなります。
230
+
231
+
232
+
233
+ event_generate では、通知を <Button> 等と同じ GUI のイベントとして得られます。
234
+
235
+ asyncio は、(内部でpollingに近いことは行われていますが)より効率的な方法で実装されてます。
236
+
237
+ createfilehandler も同様、通知の仕組みの一種です。
238
+
239
+ polling とは異なり、通知のタイミングで任意の関数を呼び出せます。
240
+
241
+ → 不要な呼び出し回数を抑制できる。
242
+
243
+
244
+
245
+ 但し、アプリ-ケーション単位で見る場合、
246
+
247
+ この部分の改善はユーザ視点では目に見えにくく、
248
+
249
+
250
+
251
+ ライブラリとして実装していて汎用的な実装にしたい場合や、
252
+
253
+ 組み込み環境で消費電力が気になる、
254
+
255
+ 等といった事情でもない限り優先度は低めになると思います。