代码之家  ›  专栏  ›  技术社区  ›  Sam Saffron James Allen

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

  •  3
  • Sam Saffron James Allen  · 技术社区  · 16 年前

    我在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。

    如何解决这个问题并确保后台线程被自动释放,即使没有显式调用Dispose?

    3 回复  |  直到 15 年前
        1
  •  4
  •   Sam Saffron James Allen    15 年前

    下面是我对这个问题的建议解决方案:

    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();
            }
        }
    }
    
        2
  •  2
  •   Andrew Kennan    16 年前

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

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

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

        3
  •  2
  •   Excel Kobayashi    16 年前

    我就是这样做的:

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