0
0

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?

More than 5 years have passed since last update.

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

Posted at

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

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

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

TaskManager.cs
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();
    }
}

TaskList.cs
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したりをしてまして、横着して以下のようなコードを書いてしまってました。

Something.cs
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、分業して開発している場合は普通に起こりえる問題ですよね。怖い怖い。

0
0
0

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
0
0

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?