在C#中自动终止非必要线程

时间:2008-12-10 03:36:31

标签: c# multithreading

我在C#中有一个对象,我需要定期执行一个方法。我希望这个方法只有在其他人使用我的对象时执行,一旦人们停止使用我的对象,我希望这个后台操作停止。

所以这是一个简单的例子(这是破碎的):

class Fish
{
    public Fish()
    {
        Thread t = new Thread(new ThreadStart(BackgroundWork));     
        t.IsBackground = true;
        t.Start();
    }

    public void BackgroundWork()
    {
        while(true)
        {
            this.Swim(); 
            Thread.Sleep(1000); 
        }
    }


    public void Swim()
    {
         Console.WriteLine("The fish is Swimming"); 
    }
}

问题在于,如果我在任何地方新建一个Fish对象,它永远不会被垃圾收集,因为有一个后台线程引用它。这是一个破损代码的插图版本。

public void DoStuff()
{ 
   Fish f = new Fish();
}
// after existing from this method my Fish object keeps on swimming. 

我知道Fish对象应该是一次性的,我应该在处理时清理线程,但是我无法控制我的调用者并且无法确保调用dispose。

如何解决此问题并确保即使未明确调用Dispose也会自动处理后台线程?

3 个答案:

答案 0 :(得分:4)

以下是我提出的解决此问题的方法:

class Fish : IDisposable 
{
    class Swimmer
    {
        Thread t; 
        WeakReference fishRef;
        public ManualResetEvent terminate = new ManualResetEvent(false);

        public Swimmer(Fish3 fish)
        {
            this.fishRef = new WeakReference(fish);
            t = new Thread(new ThreadStart(BackgroundWork));    
            t.IsBackground = true;
            t.Start();
        } 

        public void BackgroundWork()
        {
            bool done = false;
            while(!done)
            {
                done = Swim(); 
                if (!done) 
                {
                    done = terminate.WaitOne(1000, false);
                } 
            }
        }

        // this is pulled out into a helper method to ensure 
        // the Fish object is referenced for the minimal amount of time
        private bool Swim()
        {
            bool done;

            Fish fish = Fish; 
            if (fish != null)
            {
                fish.Swim(); 
                done = false;
            }
            else 
            {
                done = true;
            }
            return done;
        }

        public Fish Fish
        {
            get { return fishRef.Target as Fish3; }
        }
    }

    Swimmer swimmer;

    public Fish()
    {
            swimmer = new Swimmer(this);
    }

    public void Swim()
    {
        Console.WriteLine("The third fish is Swimming"); 
    }

    volatile bool disposed = false;

    public void Dispose()
    {
        if (!disposed)
        {
            swimmer.terminate.Set();
            disposed = true;
            GC.SuppressFinalize(this);
        }       
    }

    ~Fish() 
    {
        if(!disposed)
        {
            Dispose();
        }
    }
}

答案 1 :(得分:2)

我就是这样做的:

class Fish3 : IDisposable
{
    Thread t;
    private ManualResetEvent terminate = new ManualResetEvent(false);
    private volatile int disposed = 0;

    public Fish3()
    {
        t = new Thread(new ThreadStart(BackgroundWork));
        t.IsBackground = true;
        t.Start();
    }

    public void BackgroundWork()
    {
        while(!terminate.WaitOne(1000, false))
        {
            Swim();         
        }
    }

    public void Swim()
    {
        Console.WriteLine("The third fish is Swimming");
    }

    public void Dispose()
    {
        if(Interlocked.Exchange(ref disposed, 1) == 0)
        {
            terminate.Set();
            t.Join();
            GC.SuppressFinalize(this);
        }
    }

    ~Fish3()
    {
        if(Interlocked.Exchange(ref disposed, 1) == 0)
        {
            Dispose();
        }
    }
}

答案 2 :(得分:2)

我认为IDisposable解决方案是正确的。

如果您的类的用户不遵循使用实现IDisposable的类的指导原则,那就是他们的错 - 而且您可以确保文档明确提到应该如何使用该类。

另一个更麻烦的选项是“KeepAlive”DateTime字段,客户端调用的每个方法都会更新。然后,工作线程定期检查该字段,如果在一段时间内没有更新,则退出。当方法设置字段时,如果线程已退出,则线程将重新启动。