BETA

インスタンスの管理はちゃんとせよという教訓を得た話

投稿日:2019-12-02
最終更新:2019-12-02
※この記事は外部サイト(https://qiita.com/yuu341/items/9799e2274ab...)からのクロス投稿です

ちゃんとしたシステムを作りたい場合インスタンスというもののライフサイクルをきちんと把握しておかなければいけません。
プログラムは目に見えないものなので、インスタンスがどこで作成されて、どこで解放されるのか、それが把握できなくなるということは極端な話ですが制御不能だといってもいいでしょう。

例えばデザインパターンではインスタンス生成に関するパターンという区分けがありまして、代表的なもので言えばSingletonとかFactory(AbstractFactoryやFactoryMethod)がありますね。

今回私が作っていたシステムではスレッドの実行とインスタンス生成を組み合わせた処理がありました。
プログラムの全貌を話すことはできませんが、い摘むと下記のようなイメージです。

class TaskManager  
{  
    private CancellationTokenSource source;  
    public void Create(ISomethingProcess proc){  
        source = new CancellationTokenSource();  
        var token = source.Token;  

        Task.Run(async() =>{  
            try{  
                while(!token.IsCancellationRequested){  
                    //何かをする処理 ...  
                    proc.SomeMethod();  


                    await Task.Delay(100, token);//ちょっと待ってみたり  
                }  
            catch(TaskCanceledException e){  
                logger.Info("キャンセルされました",e);  
            }  
            catch(Exception e){  
                logger.Error("致命的なエラーが発生しました。",e);  
                throw;  
            }  
        },token);  
    }  
    public void Cancel(){  
        source.Cancel();  
    }  
}  
class TaskList  
{  
    public TaskList(IFactory factory){  
        list = new List<TaskManager>();  
        this.factory = factory;  

    }  
    private List<TaskManager> list;  
    private IFactory factory;  

    public void Start(){  
        Stop();  
        list.AddRange(factory.CreateTaskList());  
        foreach(var each in list){  
            each.Create(factory.GetSomethings());  
        }  
    }  

    public void Stop(){  
        foreach(var each in list){  
            each.Cancel();  
        }  
        list.Clear();  
    }  
}  

TaskListはコマンドによってタスクを起動したり、停止したりします。
TaskManagerはTaskを生成したり、キャンセルしたりします。

今回TaskListはいろんな処理からStartしたりStopしたりをしてまして、横着して以下のようなコードを書いてしまってました。

class Something  
{  
    public void Hoge(){  
        tasklist = new TaskList(factory);  
        tasklist.Start(); // まとめて実行  
    }  
    public void Fuga(){  
        tasklist?.Stop();  
    }  
    TaskList tasklist;  
}  

TaskList自体には何も問題ないかもしれませんがSomethingクラスではtasklistのインスタンスをHogeが実行されるたびに再生成し、スタートをしています。
私はTaskListを作る時、「StopしてからStartしてればタスクがキャンセルされるので制御できるよね!」という考えのもと作ってたんですが
Somethingを作ってた時は、「TaskList再生成すればGCされるだろうし、再生成すればいいよね~」と考えてた感じです。
この処理で、2回3回連続してHogeを実行してしまうと、見事に以前インスタンス化したTaskListが宙ぶらりんになってしまい、キャンセルできないタスクが実行しっぱなしという現象に見舞われたのでした。

今回は全部自分で作成しているシステムでしたのでもう惑わされませんが、これがTaskListとSomething、分業して開発している場合は普通に起こりえる問題ですよね。怖い怖い。

技術ブログをはじめよう Qrunch(クランチ)は、プログラマの技術アプトプットに特化したブログサービスです
駆け出しエンジニアからエキスパートまで全ての方々のアウトプットを歓迎しております!
or 外部アカウントで 登録 / ログイン する
クランチについてもっと詳しく

この記事が掲載されているブログ

@hm1gv2d26QeqWrglの技術ブログ

よく一緒に読まれる記事

0件のコメント

ブログ開設 or ログイン してコメントを送ってみよう
目次をみる
技術ブログをはじめよう Qrunch(クランチ)は、プログラマの技術アプトプットに特化したブログサービスです
or 外部アカウントではじめる
10秒で技術ブログが作れます!