スレッドの意図的な強制終了について。

タグの編集
投稿者 みどりこぶた  (高校生) 投稿日時 2017/12/1 12:20:18
スレッドの意図的な強制終了について。


Taskで実行されている処理を中断するには、予めキャンセルフラグを組み込む必要があるかと思います。
・Threading.ThreadのスレッドにはAbortメソッドがあるのに対して、Taskには強制終了の手段がない、という認識はあっていますか?
これは、中身がスレッドプールであるTaskに、スレッドを終了するという概念が無いためですか?それとも私が無知なだけで、強制終了も可能なのでしょうか?
もしくは、そもそもスレッドを強制終了する事が非推奨なため、そういった機能を排除したのですか?
もしスレッドの強制終了が非推奨なのであれば、その理由も知りたいです。


また、Taskの中身はスレッドプールである、という記事を読んだことがあります。
これはThreading.ThreadPoolを指しているのでしょうか?それとも、スレッドを使いまわしているというだけで全く別物ですか?
例えば、ThreadPoolでスレッド数の上限等を設定した場合、Taskにもそれが反映されるということですか?
・効率・コスト、仕組みなどはThreading.ThreadPoolと全く一緒と考えて良いのでしょうか?


駄文ですみません。
vb.net、vs2017で書いてます。勉強とかではなく趣味で、高校生です。常識無いかもしれませんが、指摘いただけると助かります。 
投稿者 YuO  (社会人) 投稿日時 2017/12/1 14:02:09
> ・Threading.ThreadのスレッドにはAbortメソッドがあるのに対して、Taskには強制終了の手段がない、という認識はあっていますか?

あっています。

> そもそもスレッドを強制終了する事が非推奨なため、そういった機能を排除したのですか?
> もしスレッドの強制終了が非推奨なのであれば、その理由も知りたいです。

強制終了がどのタイミングで行われているのか,外部からはわからないので,通常外部からの強制終了は推奨されません。

通常,オブジェクトの内部状態は整合性が保たれています。
これが破られるのはオブジェクトのメソッドを呼び出している最中になります。
この呼び出しの最中に強制終了されると,内部状態が不整合であるオブジェクトができてしまいます。
このような状況を起こさないように,スレッドを外部から強制終了することは,大抵のシステムにおいてできないか推奨されません。

> また、Taskの中身はスレッドプールである、という記事を読んだことがあります。
> これはThreading.ThreadPoolを指しているのでしょうか?それとも、スレッドを使いまわしているというだけで全く別物ですか?

Task自体は非同期動作のカプセル化です。
非同期I/Oなどではスレッドをほぼ利用しませんし,TaskCompletionSourceを利用すると状態の遷移自体を自分で書くことが出来ます。

で,Task.Runなどがスレッドプールを使う,というのは単純にThreadSchedular.DefaultがThreadPoolTaskScheduler,ということでしょう。
実装を読むと,TaskCreationOptions.LongRunningが付いていない場合はThreadPoolを使っています。
see)
https://github.com/Microsoft/referencesource/blob/master/mscorlib/system/threading/Tasks/TaskScheduler.cs#L288
https://github.com/Microsoft/referencesource/blob/master/mscorlib/system/threading/Tasks/ThreadPoolTaskScheduler.cs
投稿者 みどりこぶた  (高校生) 投稿日時 2017/12/2 14:56:25
解答ありがとうございます。なるほどとても分かりやすいです。

ただ、オブジェクト内部の整合性、という部分がまだいまいち理解できてません。。。
javaとかオブジェクト思考とか、そういった本等に今まで一切触れたことが無いので、
オブジェクト思考についてもう少し調べてみようと思います。

リンクありがとうございます。詳しい実装(?)、もネットで検索すればでてくるのですね。使いこなす自信はないですが、少しずつ活用できるようにしていきたいと思います。

ありがとうございました!
投稿者 shu  (社会人) 投稿日時 2017/12/4 11:18:46
> ただ、オブジェクト内部の整合性、という部分がまだいまいち理解できてません。。。
例えばファイルに書き込みを行うような処理を行っていたとして
70%位しか書き込みが完了しておらず、その状態ではファイルにアクセス出来ない
状態だったとすると強制終了されることによりそのような状態でファイルが残ってしまいます。

途中で終わらせたいとしたら何らかの方法でスレッド外から継続しないように通知をしてスレッド内で
その通知をうけたら区切りのいいところまで処理をしてから自分で処理を終了するように
します。
投稿者 みどりこぶた  (高校生) 投稿日時 2017/12/7 11:55:39
すみません、解決マーク付け忘れていました。解答いただけたのでもう少し未解決にしておきます汗。

shuさん解答ありがとうございます。
ただ、やはりピンときません。。。
仮にファイルの書き込みを行っていたとしても、TryCatchで括ってFinally句に開放処理を書いておけば大丈夫な気がします…。
また、仮にファイルをロックしたままApplication.Exit()したり、タスクマネージャーから強制終了しても、ロックは解放されるようなのですが、なぜなのでしょうか。

書き忘れたのですが、vs2017の WindowsFormアプリケーションで書いてます。
だからでしょうか…?Win32アプリケーションなど、他の言語だとメモリリーク?みたいのが起きたりするということなのでしょうか??
整合性が取れたオブジェクト、という言う意味がもう分かってません。例えば壊れたオブジェクトがあるとどういう問題が起きるのでしょうか…?

また、仮にそうなのでしたら、WindowsFormアプリケーションはどういう仕組で、その安全性を保証しているのでしょうか…?
また、スレッドの強制終了が良くない、と言われるとちょっと引っかかることがあります。スマホのホームボタンだったり、全てのアプリ終了 みたいな処理なんかはどうなってるんでしょう…。やはり定期的にフラグのようなものをチェックして、各アプリの終了処理が完了するのを待っているのでしょうか。(仕組みから何まで別物だと思いますが…)

幼い疑問、また脱線した質問ですみません。専門的でなくてもイメージ掴めればいいなと思い、思い切って返信してみました。
もしお時間有りましたら、ご享受ください。
投稿者 魔界の仮面弁士  (社会人) 投稿日時 2017/12/7 12:38:41
> TryCatchで括ってFinally句に開放処理を書いておけば大丈夫な気がします…。
Finallyブロックが処理されないケースもありえます。(強制終了時や破損状態例外など)
https://dobon.net/vb/dotnet/beginner/tryfinally.html
https://msdn.microsoft.com/ja-jp/magazine/mt620018.aspx


> 仮にファイルをロックしたままApplication.Exit()したり、タスクマネージャーから強制終了しても、ロックは解放されるようなのですが、なぜなのでしょうか。

AppDomain が Unload される際にガベージコレクトされるためです。
ただしそれはマネージコードの世界だけなので、アンマネージには適用されません。
たとえば解放順序を守る必要があるようなコードの場合、その順序が保証されるとは限りません。


> 整合性が取れたオブジェクト、という言う意味がもう分かってません。

強制終了で問題になる具体的な事例はすぐに思いつかなかったのですが、
整合性という点で言えば、たとえば、NumeriUpDown と言うコントロールが思い当たります。

このコントロールは Value プロパティで数値を管理しているわけですが、
入力制限のための最大値(Maximum)および最小値(Minimum)のプロパティがあり、
 Minimum ≦ Value ≦ Maximum
の範囲になることが保証されています。
範囲外となるような設定はできず、これが「整合性が取れた状態」にあたります。
(DataSet の EnforceConstraints プロパティのようなものです)

とはいえ、フォームデザイナーに貼り付けた時などにおいては
一時的にこの制約が取り除かれた状態が許可されているのですけれどね。

そしてこの制約判定はスレッドセーフではないため、判定処理中に
値が変化してしまうと、「整合性が取れていない状態」になりえるでしょう。


> WindowsFormアプリケーションはどういう仕組で、その安全性を保証しているのでしょうか…?

保証されていません。たとえば Excel で書き込み処理を行っている最中に
アプリが強制終了されると、ファイルの一部が不整合な状態となることがあります。

最近の Excel では、バックグラウンドで作業状態を自動バックアップすることで
そうした障害に備えるようになっていますが、それらの整合性は OS レベルではなく
アプリケーション側で対策が求められます。
投稿者 shu  (社会人) 投稿日時 2017/12/7 23:52:08
> 仮にファイルの書き込みを行っていたとしても、TryCatchで括ってFinally句に開放処理を書いておけば大丈夫な気がします…。
この部分についてですが例えファイルが正常にとじられたとしても
本来ファイルの内容が
123456789
となってなければならないのが強制終了されたが為に
12345
で終わってしまってはそのファイルを使う処理にて
データ欠落が発生することになります。
画像データであれば途中までしか表示されないとかデータが復元できずまったく表示されないとかなりますし
音声データであれば途中までしか再生されないとか音として成り立つ情報が欠落するため耳障りな音が鳴ってしまったりとか
DBにアクセスするためのコードであれば欠損しているがために正常な情報を取得できなくなります。
投稿者 みどりこぶた  (高校生) 投稿日時 2017/12/9 11:56:26
魔界の仮面弁士さん、shuさん、解答ありがとうございます。

Finally句が処理されない場合もあるのですね…、盲点ですね…、勉強になります。

>AppDomain が Unload される際にガベージコレクトされるためです。
>ただしそれはマネージコードの世界だけなので、アンマネージには適用されません。
>たとえば解放順序を守る必要があるようなコードの場合、その順序が保証されるとは限りません。
具体的にありがとうございます。すごい仕組みですね、勉強になります。
言語やOSによってもかなり違ってくる、と理解できました。

「整合性が取れていない」という意味も何となく理解できました!

ちなみにですが、最後の「Eccelで~」とはVBAのことですか?

>データ欠落が発生することになります。
フラグをチェックして処理を中断する場合でも、同じようなデータ欠落を想像してしまうのですが、どうでしょうか…?
ただ、様々な問題に繋がりかねないということですよね。
いずれにしても、動作が分かってないスレッドを強制終了することは、とても危険で色々な問題が起きる可能性があると分かりました、勉強になりました、ありがとうございました!
投稿者 (削除されました)  () 投稿日時 2017/12/11 11:25:58
(削除されました)
投稿者 魔界の仮面弁士  (社会人) 投稿日時 2017/12/11 15:38:24
何にせよ、外部からの強制終了には出来る限り頼らないようにしましょう。

たとえば BackgroundWorker で処理さえているのであれば、
CancelAsync メソッド → CancellationPending プロパティで中断通知としますし、
Task を使っているのであれば、CancellationTokenSource を利用するといった手があります。
http://www.atmarkit.co.jp/fdotnet/dotnettips/437bgwcancel/bgwcancel.html
http://note.websmil.com/vb/スレッド・タスク/vb-net-taskクラスの処理キャンセル



で、Task の中断という話とは別になりますが――たとえば System.Windows.Forms.Form の場合、
FormClosing / FormClosed イベントの引数 e.Reason を見ることで、
終了理由をある程度推し量れるかと思います。
しかしこれとて、強制終了時には通知すらされません。
https://dobon.net/vb/dotnet/form/unloadmode.html
https://msdn.microsoft.com/ja-jp/library/system.windows.forms.closereason%28vs.90%29.aspx

このとき、e.Reason に渡される値の一つに TaskManagerClosing というものがありますが、
これは、外部プロセスからの WM_CLOSE メッセージの受信を意味するものであり、
必ずしもタスクマネージャーからの終了を指し示すとは限らないのでご注意を。
https://social.msdn.microsoft.com/Forums/ja-JP/bca1296d-c35f-49d0-8d61-fbf0d45d8ee3/formclosing



> ちなみにですが、最後の「Eccelで~」とはVBAのことですか?
Eccel ではなく、Excel ですね。

強制終了によって整合性に問題が生じるという話は、
開発言語が VBA だろうと MFC だろうと何だろうと、根本的には同じことです。


一応、『ファイルの入出力最中に停止しても、ファイルシステムが破損しないこと』は
OS レベルで保証されていますが、書き込み内容が中断されることについては避けられません。

それに、いくら Windows が、OS レベルでのファイルシステムの一貫性を保証していたとしても、
書き込み中に電源が切られたりすれば、ファイルシステムの一貫性すら保証されませんよね。

そこで Windows は、強制的に電源が切られた後の次回起動時に、不整合の検査のため
スキャンディスク等によるファイルシステムのチェックと修復を試みる仕組みが用意されています。

Excel というアプリも同様です。前回終了時に正しく終了されていなかったことを
検知した場合には、自動退避しておいた一時ファイル等の情報を読み取った上で、
それらをユーザーに提示して、どのデータを残すのか目視チェックを促すように設計されています。

また、ここでは話を分かりやすくするためにファイル操作を例として挙げていますが、
実際に生じる問題としては、ネットワーク通信やオブジェクトハンドルなど多岐に渡ります。


> フラグをチェックして処理を中断する場合でも、同じようなデータ欠落を想像してしまうのですが、どうでしょうか…?
通常の終了時であれば、終了通知なり Finally なりによって、
後始末するための猶予が与えられますが、強制終了ではそうも行きませんね。

強制終了に備える方法の一案としては、作業状態を中断できる機能を設けておき、
次回起動時にはその状態から復帰できるようにしておくといった手段があります。

たとえば作業中の処理を、ファイルなりクラウドなりに随時残しておき、
次回起動時にはそれを読み取ることで、前回の処理が中断したのか
正常終了したのかを判断し、必要なら前回の続きから再開させるわけです。

逆に言えば、前回の処理結果を保持するような仕組みが無いのなら、
今までの作業内容が単に失われるのみであるとも言えます。
この辺のさじ加減は、アプリケーション要件によるところですね。
(たとえば、電卓アプリを強制終了したときに、前回の計算式を残しておく
 必要があるのか、最初から打ち直してもらって構わないのか、といった具合に)

とはいえ、強制終了が悪手であることは変わりません。
たとえばアンマネージリソース(オブジェクトハンドルなど)を使っていた場合、
それらを処分することなく強制終了してしまうと、その分のリソースが解放漏れに
なってしまい、OS を再起動するまで再利用できない状態に陥る可能性がありえます。

※リソースリークは、強制終了時にのみ起こるわけではありません。
 確保したリソースが処分される前に、新たなリソースが次々と
 確保するようなシステムであれば、それもリークと言えます。
投稿者 みどりこぶた  (高校生) 投稿日時 2017/12/16 01:01:49
返信遅くなりすみません。

とても丁寧な返答ありがとうございます。
例えから何まで、とてもわかり易く、疑問の欠片もありません。
繰り返し繰り返し、読まさせていただきますm(_ _)m
いつもいつも解答ありがとうございますm(_ _)m