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

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

新規登録して質問してみよう
ただいま回答率
85.37%
スクレイピング

スクレイピングとは、公開されているWebサイトからページ内の情報を抽出する技術です。

タイムアウト

タイムアウトはイベント発生から完了までに掛かる経過時間に対する一定の待ち時間を指します。また、特定の時間が経過された場合に発生するイベントを指すこともあります。

マルチスレッド

マルチスレッドは、どのように機能がコンピュータによって実行したのかを、(一般的にはスレッドとして参照される)実行の複合的な共同作用するストリームへ区分することが出来ます。

Python

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

Q&A

解決済

3回答

4341閲覧

python schedule使用時のtimeoutについて

help_heaven

総合スコア9

スクレイピング

スクレイピングとは、公開されているWebサイトからページ内の情報を抽出する技術です。

タイムアウト

タイムアウトはイベント発生から完了までに掛かる経過時間に対する一定の待ち時間を指します。また、特定の時間が経過された場合に発生するイベントを指すこともあります。

マルチスレッド

マルチスレッドは、どのように機能がコンピュータによって実行したのかを、(一般的にはスレッドとして参照される)実行の複合的な共同作用するストリームへ区分することが出来ます。

Python

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

0グッド

1クリップ

投稿2021/05/15 16:14

pythonでscheduleを使用して定期的にスクレイピングをしています。
タイムアウト処理を実装しようと思い、
https://webbibouroku.com/Blog/Article/python-timeout#outline__2
こちらのウェブページを拝見しました。
この中ではマルチスレッドを以前に少しだけ触ったことがあるので、マルチスレッドを利用した方法で試してみました。
scheduleを使用しなければ期待通りの動きになるのですが、scheduleを使用するとデーモン化しても裏でまだスレッドが普通に生きてます。
スケジュール通りに新しいスレッドで作業を開始してくれるので一応の解決はしたのですが、裏でタイムアウトしたスレッドが生きていて気持ち悪いです。
threadは殺すのは良くないということですが、解決方法を探しています。
他にもtimeout-decoratorやらも拝見しましたが、どうもwindowsだと使用できない?とかで諦めました。
サンプルとしてこのようなコードで試しました。

import time import threading import schedule def subjob(text): print(text) if text == "bbb": print("これはbbbです") time.sleep(15)#レスポンスが遅すぎるのでフリーズしたとする print("フリーズしているでしょう") def mainjob(): subjob("aaa") subjob("bbb") subjob("ccc") def timeout_test(): thread1 = threading.Thread(target=mainjob) thread1.setDaemon(True) thread1.start() thread1.join(timeout=3) if thread1.is_alive: print("タイムアウトしました") return print("終わりました") schedule.every(10).seconds.do(timeout_test) while True: schedule.run_pending() time.sleep(1)

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

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

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

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

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

guest

回答3

0

ベストアンサー

コードの問題点:

thread.is_alive() はメソッドなので呼び出しが必要です。
if thread.is_alive: は常に真になります。

裏でタイムアウトしたスレッドが生きていて気持ち悪いです。

スレッドが残っているのは正常な動作です。

thread.join(timeout=3) でのタイムアウトは、
呼び出し側で、スレッドの終了まで待機しているブロッキング状態の解除であって、
対象のスレッドが行っている処理には影響しません。

スレッドを安全に終了したい場合、中断処理は自分で実装する必要があります。

threadは殺すのは良くないということですが、解決方法を探しています。

Python 標準ライブラリ スレッドベースの並列処理

注釈 デーモンスレッドは終了時にいきなり停止されます。デーモンスレッドで使われたリソース (開いているファイル、データベースのトランザクションなど) は適切に解放されないかもしれません。きちんと (gracefully) スレッドを停止したい場合は、スレッドを非デーモンスレッドにして、Event のような適切なシグナル送信機構を使用してください。

前提として、Threadを殺すのは良くない →
強制終了は「リソースの解放が適切に行われないかもしれないので」良くない

質問の文章から、タイムアウトでスレッドの処理が中断されるのを
期待されてるよに見受けられますが、仮にそのような振る舞いをした場合は、
リソースの解放処理が適切に行われないので、
「良くない」とされるスレッドの終了方法に該当してしまいます。

例えば、スレッドで実行する処理を、
以下の様に置き換えて考えて見て下さい。

python

1 subjob("aaa") # リソース所得 ファイルopen等 2 subjob("bbb") # 時間の掛かる処理 3 subjob("ccc") # リソース解放 ファイルclose等

join(timeout=3) は、subjob("bbb") の途中で待機状態を解除するとします。

裏でタイムアウトしたスレッドが生きていて気持ち悪いです。

の「タイムアウトしたスレッド」が誤解だと思いますが、
(タイムアウトするのは、呼び出し側スレッドでの待機状態)

「~生きていて気持ち悪いです」ということは、
subjob("ccc") の部分は、実行してほしくないという事ですよね。
そうすると、subjob("bbb") で処理を中断することは、実際の処理に当てはめると
リソースの解放が行われないので、良くないスレッドの終了方法という事になります。

threadは殺すのは良くないということですが、解決方法を探しています。

おそらく、質問の意図は、
リソースの解放を考慮した「きちんとしたスレッドの停止」を行いたいのでしょうか。

回答としては、まずはスレッドで行う処理中に、
解放が必要なリソースがあるかどうか確認したうえで、

スレッド内で行う処理は、後処理が確実に実行されるように、
安全に中断可能な設計にする必要があります。

join(timeout) の後に、is_alive() を確認して、
生存(タイムアウト)だった場合は、
スレッドの処理を完了させるための通知を行います。

  • 簡易的な方法であれば、フラグを設定してループを抜ける等
  • signal で割り込み、例外を投げる (多分 windowsでは未対応)

但し、ライブラリ等の関数内で時間が掛かる処理がある場合は、
前者の方法では対応できません。

追記2:
質問のコードには書かれてませんが、
スクレイプという事なので、requests 等のライブラリをお使いでしょうか?
もし、ライブラリ側で対応しているなら、そちらの ​timeout オプションの利用が適してます。

thread.join(timeout=3) は、スケジュールのループを3秒間ブロックするので、
秒単位のスケジュールがある場合、3秒遅延になります。

ライブラリ側でのタイムアウトであれば、(質問のコードの構成の場合)別スレッドなので、
メインスレッドのスケジュールのループに影響ありません。


concurrency モジュールに言及しておくと、
concurrency.future には cancel() がありますが、
スケジュールされた実行前のタスクのキャンセルであって、
実行中の処理の中断とは異なります。

別の選択肢としては、非同期IOの導入
asyncio.Task の cancel() は、
実行中の処理の中断という用途には近いかもしれません。
asyncio対応ライブラリであれば、I/O処理待ちのタイミングでの中断が可能です。


追記

scheduleを使用するとデーモン化しても裏でまだスレッドが普通に生きてます。

ここも恐らく誤解だと思います、
どのようにスレッドの生存を確認したのでしょうか。

schedule の利用の有無に関わらず、デーモンスレッドが破棄されるのは、
「デーモンでない生存中のスレッドが全てなくなる」タイミングなので、
メインスレッドが終了した後です。

投稿2021/05/16 01:03

編集2021/05/17 02:05
teamikl

総合スコア8681

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

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

0

threadは殺すのは良くないということですが、解決方法を探しています。

フリーズしても安全に殺すことを考えるならプロセスで並列実行するべきでしょう。
(python なら multiprocessing)

threadを使うなら、殺す事態にならないように工夫する(何かエラーが発生しても
タイムアウトで終了するよう実装するなど)のが正しいと思います。

投稿2021/05/15 23:33

編集2021/05/15 23:36
sigsegv

総合スコア895

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

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

0

おはようございます。

問題文読ませていただきました。

sys.exit()を使って、終了するよう命令すると良さそうですね。

こちらの記事がすごく参考になります。
Pythonのthreading使ったらプログラムが止まらなかったパターンがある

投稿2021/05/15 22:19

退会済みユーザー

退会済みユーザー

総合スコア0

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

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

あなたの回答

tips

太字

斜体

打ち消し線

見出し

引用テキストの挿入

コードの挿入

リンクの挿入

リストの挿入

番号リストの挿入

表の挿入

水平線の挿入

プレビュー

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

ただいまの回答率
85.37%

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

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

質問する

関連した質問