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

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

新規登録して質問してみよう
ただいま回答率
85.51%
C#

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

Q&A

2回答

1066閲覧

メインクラスで管理しているサブクラスのインスタンスを削除した際の挙動・動作について

kq.56

総合スコア1

C#

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

0グッド

2クリップ

投稿2022/06/26 12:48

お世話になります。

メインクラスでサブクラスのインスタンスを生成し、Dictionaryで保持し管理しようと考えています。

サブクラスにはTimerが存在し、そのTimerがタイムアウトした際にとある処理を行い、
その後メインクラスから引数として渡されたCallback関数を呼び出します。

コールバック関数が呼び出されたらメインクラスで保持しているDicitonaryから削除し、Dictionaryで持っているクラスは今使用しているサブクラスのみとしたいです。

以下、コード例です。(メインクラスのコンストラクタは適当です)

// メインクラス public class MainClass { private ConcurrentDictionary<int, SubClass> mClassMngDic = new ConcurrentDictionary<int, SubClass>(); private Timer mWaitTimer; public MainClass() { var subclass = new SubClass(1, CallbackFunc); mClassMngDic.TryAdd(1, subclass); mWaitTimer = new Timer(WaitTimerCallback, null, 10 * 1000, Timeout.Infinite); Console.ReadKey(); } private void WaitTimerCallback(object obj) { if (mClassMngDic.Count == 0) { mWaitTimer.Change(Timeout.Infinite, Timeout.Infinite); } } private void CallbackFunc(int index) { // Dicitonaryから消す SubClass subclass; mClassMngDic.TryRemove(index, out subclass); } // サブクラス private class SubClass { public delegate void CallbackFunc(int index); private CallbackFunc mCallback; private int mIndex; public SubClass(int index, CallbackFunc callback) { mIndex = index; mCallback = callback; new Timer(TimerCallback, null, 5 * 1000, Timeout.Infinite); } private void TimerCallback(object obj) { // 重い処理を実行① mCallback(mIndex); // 重い処理を実行② } }

質問したいのは以下です。

  1. サブクラスで処理している途中にメインクラスのDictionaryからサブクラスを削除した際、GCが発生した時にサブクラスのインスタンスが確保しているメモリは解放対象となるのでしょうか。
  2. 1の質問がYESの場合、サブクラスで処理している途中にメインクラスのDictionaryからサブクラスを削除したと同時にGCが発生し、メモリ開放が発生した際、Timerタイムアウト時の処理(TimerCallback)の続きの処理を実行できるのでしょうか。
  3. サブクラスで処理している途中にメインクラスのDictionaryからサブクラスを削除した際、Timerタイムアウト時の処理(TimerCallback)の続きの処理を実行できるのでしょうか。

色々なサイトを調べてみたのですが、答えを見つけることができませんでした。
ご存じの方、ご教示いただけませんでしょうか。

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

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

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

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

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

退会済みユーザー

退会済みユーザー

2022/06/27 00:20

質問する際は何を何で作っているかを質問の一行目に書きましょう。(例: Windows OS で動くコンソール アプリを Visual Studio 2022 を使ってフレームワークを .NET Framework 4.8 として作っています・・・とか)
退会済みユーザー

退会済みユーザー

2022/06/27 02:06

上の質問のコード、 var subclass = new SubClass(1, CallbackFunc); mClassMngDic.TryAdd(1, subclass); は実際と同じですか? MainClass のフィールド subclass に new SubClass(1, CallbackFunc) で生成した SubClass のインスタンスへの参照が保持され続けるのですか? たぶん実際は違うのではないかと思っているのですが・・・
guest

回答2

0

「サブクラスで処理している途中に」というのが「TimerCallback を実行中」というのであれば、1,2,3 の回答は「解放されない」ということになります。

「ガベージ コレクションの基礎」
https://docs.microsoft.com/ja-jp/dotnet/standard/garbage-collection/fundamentals
の「メモリの解放」のところに
「アプリケーションのルートには、静的フィールド、スレッドのスタック上のローカル変数、CPU レジスタ、GC ハンドル、ファイナライズ キューが含まれています。」
という記述があります。

メソッド実行中は、解放対象にはなりません。

ただし、SubClass が Timer のインスタンスを保持していないので、危ういコードになっています。
次のように SubClass のインスタンスを参照していても、Timer が発動する前に GC が走ると Timer のインスタンスが解放され、TimerCallback が呼ばれません。

C#

1using System; 2using System.Threading; 3 4class Program 5{ 6 static void Main(string[] args) { 7 var s = new SubClass(); 8 GC.Collect(); 9 GC.WaitForPendingFinalizers(); 10 Console.WriteLine("何かキーを押してください。"); 11 Console.ReadKey(); 12 } 13} 14 15class SubClass 16{ 17 public SubClass() { 18 new Timer(TimerCallback, null, 5 * 1000, Timeout.Infinite); 19 } 20 21 private void TimerCallback(object obj) { 22 Console.WriteLine("TimerCallback"); 23 } 24}

下のように Timer のインスタンスを保持すれば TimerCallback は実行されます。
IDE0052 の警告が出るので抑止してください。

C#

1 class SubClass 2 { 3#pragma warning disable IDE0052 4 private readonly Timer timer; 5#pragma warning restore IDE0052 6 7 public SubClass() { 8 timer = new Timer(TimerCallback, null, 5 * 1000, Timeout.Infinite); 9 }

投稿2022/06/27 02:19

編集2022/06/27 09:00
KOZ6.0

総合スコア2622

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

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

退会済みユーザー

退会済みユーザー

2022/06/27 03:03

質問の、 > 1. サブクラスで処理している途中にメインクラスのDictionaryからサブクラスを削除した際、GCが発生した時にサブクラスのインスタンスが確保しているメモリは解放対象となるのでしょうか。 については、質問のコード、 var subclass = new SubClass(1, CallbackFunc); mClassMngDic.TryAdd(1, subclass); がホントにそうなっているかが一番のポイントではないのでしょうか 
KOZ6.0

2022/06/27 05:56

質問1の意味は、サブクラスのインスタンスが外部のどこからも参照されなくなったときにGCが発生するとどうなるか?ということですね。 提示されたコードには、その部分はありません。
KOZ6.0

2022/06/27 06:03

で、その回答としては、Timer 発動前なら Timer のインスタンスが回収され TimerCallback がよばれなくなってしまう、ということになります。
退会済みユーザー

退会済みユーザー

2022/06/27 08:10 編集

> 質問1の意味は、サブクラスのインスタンスが外部のどこからも参照されなくなったときにGCが発生するとどうなるか?ということですね。 自分の理解はそうではなくて、質問に書いてある、 > 1. サブクラスで処理している途中にメインクラスのDictionaryからサブクラスを削除した際、GCが発生した時にサブクラスのインスタンスが確保しているメモリは解放対象となるのでしょうか。 の意味は、MainClass のコンストラクタで、 var subclass = new SubClass(1, CallbackFunc); mClassMngDic.TryAdd(1, subclass); として SubClass のインスタンスを生成して Dictionary に追加した後、CallbackFunc で、 // Dicitonaryから消す SubClass subclass; mClassMngDic.TryRemove(index, out subclass); のように Dictionary から取り除いたとき、MainClass のコンストラクタで生成した SubCalss のインスタンスは GC の対象になるかという意味だと思っているのですが。 (質問のコメントに書きましたが、たぶん実際のコードは違っていて var subclass = new SubClass(1, CallbackFunc); というようなコードは無いのではと疑ってはいるのですが・・・)
KOZ6.0

2022/06/27 08:11

なるほど、「サブクラスで処理している途中」というのは「TimerCallback を実行中に」ということですか。 それなら質問1,2,3は、ほぼ同じ内容で、解放されない、という回答になりますね。
guest

0

1 なります
2 削除したと同時にGCが発生、するとは限りません。で、続きの処理を実行できません。
そこらへんの手当を全部済ませてから、削除する必要があります

#ってそもそも、わざわざ自分で削除する必要もないです

投稿2022/06/26 15:26

編集2022/06/26 15:28
y_waiwai

総合スコア87715

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

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

kq.56

2022/06/26 23:53 編集

ご回答ありがとうございます。
Zuishin

2022/06/26 15:58

嘘つくのやめてもらっていいですか? 低評価しました。
Zuishin

2022/06/26 16:09 編集

この人 C# 知らずに適当なこと言ってるだけなので、簡単に信じないでください。 TimerCallback はインスタンスメソッドで、暗黙の変数 this にインスタンスを保持しています。 つまりこの処理が終わるまでインスタンスが解放されることはなく、GC 対象になることはあり得ません。
KOZ6.0

2022/06/27 02:20 編集

回答に記載しました。
guest

あなたの回答

tips

太字

斜体

打ち消し線

見出し

引用テキストの挿入

コードの挿入

リンクの挿入

リストの挿入

番号リストの挿入

表の挿入

水平線の挿入

プレビュー

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

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

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

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

ただいまの回答率
85.51%

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

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

質問する

関連した質問