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

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

新規登録して質問してみよう
ただいま回答率
85.48%
.NET Core

.NET Coreは、マネージソフトウェアフレームワークでオープンソースで実装されています。クロスプラットフォームを前提に考えられており、Windows/Mac/Linuxで動くアプリケーションを作成することが可能です。

C#

C#はマルチパラダイムプログラミング言語の1つで、命令形・宣言型・関数型・ジェネリック型・コンポーネント指向・オブジェクティブ指向のプログラミング開発すべてに対応しています。

Q&A

3回答

5696閲覧

.NET CoreのSystem.Threading.Thread.Abort()の処理について

appdev

総合スコア16

.NET Core

.NET Coreは、マネージソフトウェアフレームワークでオープンソースで実装されています。クロスプラットフォームを前提に考えられており、Windows/Mac/Linuxで動くアプリケーションを作成することが可能です。

C#

C#はマルチパラダイムプログラミング言語の1つで、命令形・宣言型・関数型・ジェネリック型・コンポーネント指向・オブジェクティブ指向のプログラミング開発すべてに対応しています。

0グッド

0クリップ

投稿2020/06/05 12:42

編集2020/06/06 14:23

System.Threading.Thread.Abort()の処理を実装したいと考えております。

現在、開発中のシステムでは、C#でからsshでLinuxへ接続して、
過去に開発された、コンソール画面でGUIっぽく見せたシステムを操作させています。
通常のsshのレスポンスと異なる点は、
System.IO.Stream.ReadByte()でレスポンスを受け取る際に既にすべて受信していると、
C#側ではレスポンス待ちのままフリーズしてしまいます。
System.IO.Stream.Readでも、レスポンス以上のバイトを取得しようとすると同様にフリーズしてしまいます。
ここの回避策としては、リクエストごとに想定されるレスポンスメッセージパターンを作成し
必要分取得したらReadByte()を呼ばない様な対応をしてきました。

今回、System.Threading.Thread.Abort()が必要になったのは、
想定されるパターンが多すぎて、網羅しきれない場合を考慮し、
フリーズ発生時にAbortをかけようとしています。

ですが、残念ながら.NET coreでは、上記機能をサポート対象から外してしまったそうで、
代わりに、CancellationTokenが利用できる非同期処理を呼び出すようにしている様です。
Thread.Abort()だと同じ行でフリーズしてしまうため、CancellationTokenを指示しても、
処理を抜けることが出来ない状態となっております。

.NET CoreでもThread.Abort()を呼び出す方法があるのでしょうか?
他にも回避策があれば、教えていただきたいです。


2020/06/06追記
説明が不足しており申し訳ございません。

想定されるパターンが多すぎて

想定されるパターンは、入力に対するレスポンスのパターンです。
イメージとしてはTeratermでssh接続し、Linux側のアプリケーションを起動すると指定の行・列情報や文字色などのルールをバイト単位で返却して画面描画を行っています。
利用者は、全てキーボードでメニュー選択などを行い対話することが可能です。
このアプリケーションの問題点は、レスポンスが終わったときにレスポンス完了した旨がわかるバイトコードを返さないという点です。

ssh接続を行いlsコマンドを受け取ったりする際においては、読取処理でフリーズすることはないかと思います。
上記のアプリでは、最後のバイトコードが普通に画面表示を行うための文字を返している可能性もあり得ます。
例えば、http/2のプロトコルにおいて、レスポンスを返す際は、レスポンス単位にフレームがついて返却されるため、
レスポンスのメッセージだけでなく付随する情報があるかと思います。
それらのフレームの規格が全くない状態のため、メッセージを返却し終わったかどうかのルール作れないです。

CancellationTokenパターンで以下の様に試してみた。
このパターンだと、フリーズ発生前にcts.Cancel()を呼び出すとExceptionを発生させてくれて動作を止めることを確認しました。
しかし、上記のメッセージを返却し終わった後に、cts.Cancel()を呼び出してもExceptionは発生せず、
レスポンスを受け取ろうとするとそこで止まってしまいます。

トークンソースの作成

CancellationTokenSource cts = new CancellationTokenSource();

Task内でReadAsyncにセット

var result = await stream.ReadAsync(buffer, 0, buffer.Length, cts.Token);

キャンセル呼び出し

cts.Cancel();

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

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

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

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

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

guest

回答3

0

通常のsshのレスポンスと異なる点は、

System.IO.Stream.ReadByte()でレスポンスを受け取る際に既にすべて受信していると、
C#側ではレスポンス待ちのままフリーズしてしまいます。
System.IO.Stream.Readでも、レスポンス以上のバイトを取得しようとすると同様にフリーズしてしまいます

StreamがNetworkStreamであれば、ReadTimeoutを設定すれば止まらないと思います。
デフォルトではInfiniteに設定されています。

投稿2020/06/06 16:35

退会済みユーザー

退会済みユーザー

総合スコア0

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

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

0

こんにちは。

Thread.Abort() はヘタに使うと簡単にプログラムを不正な状態に出来てしまうため、細部まで動作を考慮した上で利用が妥当である場合を除いて、使ってはいけないものです。
Stream の入力待ちに関しては、Stream.ReadAsync に CancellationToken を渡すことで任意のタイミングで中断できるため、そもそも Thread.Abort() を使う必要性は全くありません。


今回、System.Threading.Thread.Abort()が必要になったのは、

想定されるパターンが多すぎて、網羅しきれない場合を考慮し、
フリーズ発生時にAbortをかけようとしています。

「想定されるパターン」というのが入力値の想定に関する話であれば、仰るとおり中断処理によって一括で解決できるものです。単に Abort の代わりに CancellationToken を使ったキャンセル処理を利用すれば良いだけです。

Thread.Abort()だと同じ行でフリーズしてしまうため、CancellationTokenを指示しても、

処理を抜けることが出来ない状態となっております。

「CancellationTokenを指示」「処理を抜けることが出来ない」とは一体何でしょうか。あなたが何をしたのかが読み取れず、そもそも .NET Core では Thread.Abort() は Unsupported なので、文章自体が意味不明になっています。

投稿2020/06/06 09:04

tamoto

総合スコア4103

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

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

appdev

2020/06/06 12:26

ご回答ありがとうございます。 申し訳ございません。説明を追記させて頂いたのでご確認頂けるととてもありがたいです。 Thread.Abort()はサポートされてないんですよね・・・Exceptionでも確かにそうなってはいたのですが、例えば、クラスorメソッドあたりに何か属性を付与して呼び出したりすれば、動作してくれないのかを期待していました。 勉強不足ですがアンマネージドコードがそんな雰囲気だったかなと思っていましたので…
tamoto

2020/06/06 12:55

追記のような状況で、Abort を使って強制終了することを考えているのであれば、代わりに CancellationToken を使ったキャンセル処理を使えば良いだけです。Abort で行おうとしている操作を代替できます。
appdev

2020/06/06 14:37

アドバイス頂きありがとうございます。 CancellationTokenを試したところ、メッセージを受信し終わるとやはりフリーズが発生してしまいました。 CancellationTokenの仕組みとしては、Cancelリクエストが発生していないかを定期的に監視することで、実現されているという認識でおります。よって、特定の処理で止まってしまった場合においてはCancelリクエストを出しても受け付けてくれないというイメージでいます。 上記の理由から、Abortに比べて安全で推奨されているかと思っていました。私の認識に間違いが御座いましたらご指摘頂けるとありがたいです。 また、欲しいデータはbufferに入るため、戻り値は無視することにして、 タイムアウトしたらそのスレッドはキャンセルリクエストを投げたまま放っておく方針でもいいのかなという気がしてきました。プロセスのライフサイクルは短いので、スレッドが溜まっても大した問題にはならないのかなと思っています。
tamoto

2020/06/08 02:16

コードの断片しか提示していただけていないので全容が全く見えていませんが、CancellationToken は対応する非同期メソッドが無限に待機していたとしてもシャットダウンできますが、Cancellation の対象外部分でブロックしているのであればそのような状況には対処できません。 > CancellationTokenの仕組みとしては、Cancelリクエストが発生していないかを定期的に監視することで、実現されているという認識でおります。 一部正しいです。Cancellation をサポートしている一部の API (Stream.ReadAsync など) はキャンセルがリクエストされたタイミングで割り込んでシャットダウンを行います。 また、キャンセルが呼び出されたタイミングで何か任意の処理を実行することでキャンセル処理を行うことも可能です。 現状どのようなフリーズが発生しているのかが伝わっていないのでこれ以上のコメントは難しいです。 CancellationToken の使い方が良くないだけで解決可能なものだと思いますが、コードがなければ提案することは不可能です。
退会済みユーザー

退会済みユーザー

2020/06/08 02:55 編集

Stream.ReadAsyncの実装を見ると、呼び出し直後しかCancellationTokenをチェックしていないので、Streamの継承先でReadAsyncのオーバーライドとキャンセル処理を実装していなければキャンセル出来る訳ではないです。ReadAsyncでCancellationTokenを渡してキャンセル出来るかどうかは、Stream継承先の実装に依存します。 https://referencesource.microsoft.com/#mscorlib/system/io/stream.cs,e224b4bec8748849,references
tamoto

2020/06/08 03:21

abstract な Stream 側のメソッドですが、なるほどそうなっているんですね。 確かに Cancellation が必ず行われる保証があるわけではないですね。すみません。 ただ、Microsoft 提供の非同期 Stream は一通り ReadAsync の独自実装を持っていた気がします。 結局この質問で何をどう使っているのか分からないので解決できるかどうかも分かりませんね。
退会済みユーザー

退会済みユーザー

2020/06/08 04:16

提示されてるソースでは、Streamの種類が不明なので何とも言えませんね。ただ、NetworkStreamには、少なくとも実装されていないようです。自分で独自実装したStreamではないなら、正直CancellationTokenが処理されるのはあまり期待しない方がいいですね。ブロッキングだけが問題であれば、ReadTimeoutのタイムアウト処理で十分かと思います。
退会済みユーザー

退会済みユーザー

2020/06/08 05:00 編集

なるほど、History見るとOct 8, 2015にReadAsyncのオーバーライドが追加されているんですね。ILSpyで雑に見てるから気づきませんでした。(v4.0_4.0.0.0__b77a5c561934e089\System.dll) 最近のランタイムなら大丈夫って事ですかね。
tamoto

2020/06/08 05:09

.NET Core 系はネイティブ付近の内部実装はだいぶ作り直しされていたりするので、雑だったり懸念だったりする部分はだいぶ少なくなっています。Thread.Abort みたいな危ない API を潰してしまうあたりそこそこ信頼できると思いますね。
appdev

2020/06/10 21:20 編集

tomatoさん、radianさん 様々な議論をして頂きありがとうございます。 とりあえずの回避策として、Task.RunでReadAsyncで1バイトずつバッファを取り出すようにしてはいますが、 やはり根本的な解決方法ではないので。。。 おそらくssh接続でviとかlessを実行してreadすると再現するのではないか・・・ と考えてはいるので、サンプルの作成をしてみたいと思います。
guest

0

readを非同期で読取してみたらIOブロッキングでフリーズは起こらなくなると思いますがどうでしょうか。
ちょっと使い方にクセがありますけど。
NetworkStream.BeginRead
NetworkStream.ReadAsync

BeginReadは読み込み完了した後コールバック関数が呼ばれます。
ReadAsyncはTaskで管理するか、awaitで読み込み完了までブロックさせずに待つこともできます。
試してみたことはないですがCancellationTokenを渡せるので読み込み待ちのキャンセルもできるかもしれません。
readBytesを非同期処理に逐次置き換えるのが工数上厳しいなら、await ReadAsyncに機械的に置き換えてブロッキングによるフリーズを抑止するのも一つの手かと思われます。

投稿2020/06/05 13:27

hope_mucci

総合スコア4447

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

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

appdev

2020/06/06 12:03

ご回答頂きありがとうございます。 頂いた内容で検証をしてみたいと思います。
guest

あなたの回答

tips

太字

斜体

打ち消し線

見出し

引用テキストの挿入

コードの挿入

リンクの挿入

リストの挿入

番号リストの挿入

表の挿入

水平線の挿入

プレビュー

まだベストアンサーが選ばれていません

会員登録して回答してみよう

アカウントをお持ちの方は

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

ただいまの回答率
85.48%

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

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

質問する

関連した質問