[Architecture Design] DI Thread Tips


套用IoC模式

 

在设计系统对象的时候,可以套用IoC模式来切割相依性。如下列范例程序代码,就是在Master、Slave两个对象之间套用IoC的小小范例,在这个范例中NormalSlave会透过MessageNotified事件,来将执行讯息通知给Master。

 

 

 

using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;

namespace ConsoleApplication1
{
    class Program
    {
        static void Main(string[] args)
        {
            // Slave
            ISlave slave = new NormalSlave();

            // Master
            Master master = new Master(slave);

            // Execute
            master.Execute();

            // Wait
            System.Threading.Thread.Sleep(5000);
            Console.WriteLine("End");            
        }
    }

    public class Master
    {
        // Fields
        private readonly ISlave _slave = null;


        // Constructors
        public Master(ISlave slave)
        {
            #region Contracts

            if (slave==null) throw new ArgumentException();

            #endregion

            // Slave
            _slave = slave;
            _slave.MessageNotified += this.Slave_MessageNotified;
        }
               

        // Methods 
        public void Execute()
        {
            // Slave
            _slave.Execute();
        }


        // Handlers
        private void Slave_MessageNotified(string message)
        {
            #region Contracts

            if (string.IsNullOrEmpty(message) == true) throw new ArgumentException();

            #endregion

            // Print
            Console.WriteLine(string.Format("{0} {1}", DateTime.Now.ToString("HH:mm:ss"), message));
        }
    }

    public interface ISlave
    {
        // Methods 
        void Execute();


        // Events
        event Action<string> MessageNotified;
    }

    public class NormalSlave : ISlave
    {
        // Methods 
        public void Execute()
        {
            this.OnMessageNotified("Work");
        }


        // Events
        public event Action<string> MessageNotified;
        private void OnMessageNotified(string message)
        {
            #region Contracts

            if (string.IsNullOrEmpty(message) == true) throw new ArgumentException();

            #endregion

            var handler = this.MessageNotified;
            if (handler != null)
            {
                handler(message);
            }
        }        
    }
}



注入包含Thread的实做

 

既然套用了IoC模式,就是为了后续可以注入不同的实做。这边假设要注入一个实做是:ThreadSlave会透过MessageNotified事件,来将存活讯息定时通知给Master。而为了完成这个实做中的「定时通知」功能,在ThreadSlave中开启一条Thread,用以定时执行通知。依照到目前为止的分析设计,可以建立出下列范例程序代码。

 

单从程序代码去分析下列的范例程序,会发现逻辑都是正确的。但是在执行之后马上就会发现,因为在ThreadSlave里面开启了一条Thread,可是却没有程序代码去关闭Thread,所以这条Thread会持续的执行下去,而造成应用程序无法关闭。

 

 

using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;

namespace ConsoleApplication2
{
    class Program
    {
        static void Main(string[] args)
        {
            // Slave
            ISlave slave = new ThreadSlave();

            // Master
            Master master = new Master(slave);

            // Execute
            master.Execute();

            // Wait
            System.Threading.Thread.Sleep(5000);
            Console.WriteLine("End");
        }
    }

    public class Master
    {
        // Fields
        private readonly ISlave _slave = null;


        // Constructors
        public Master(ISlave slave)
        {
            #region Contracts

            if (slave == null) throw new ArgumentException();

            #endregion

            // Slave
            _slave = slave;
            _slave.MessageNotified += this.Slave_MessageNotified;
        }


        // Methods 
        public void Execute()
        {
            // Slave
            _slave.Execute();
        }


        // Handlers
        private void Slave_MessageNotified(string message)
        {
            #region Contracts

            if (string.IsNullOrEmpty(message) == true) throw new ArgumentException();

            #endregion

            // Print
            Console.WriteLine(string.Format("{0} {1}", DateTime.Now.ToString("HH:mm:ss"), message));
        }
    }

    public interface ISlave
    {
        // Methods 
        void Execute();


        // Events
        event Action<string> MessageNotified;
    }

    public class ThreadSlave : ISlave
    {
        // Fields
        private readonly System.Threading.Thread _thread = null;


        // Constructors
        public ThreadSlave()
        {
            // Thread
            _thread = new System.Threading.Thread(this.Operation);
            _thread.Start();
        }

        // Methods 
        public void Execute()
        {
            this.OnMessageNotified("Work");
        }

        private void Operation()
        {
            while (true)
            {
                System.Threading.Thread.Sleep(1000);
                this.OnMessageNotified("Alive");
            }
        }


        // Events
        public event Action<string> MessageNotified;
        private void OnMessageNotified(string message)
        {
            #region Contracts

            if (string.IsNullOrEmpty(message) == true) throw new ArgumentException();

            #endregion

            var handler = this.MessageNotified;
            if (handler != null)
            {
                handler(message);
            }
        }
    }
}



加入关闭Thread的Stop方法

 

上一个范例是因为开了Thread,却没有去关闭Thread,所以会造成应用程序无法关闭,那就加入Stop方法来关闭Thread让程序正常关闭。依照这样的分析设计,可以建立出下列范例程序代码。

 

这个范例程序代码,可以定时执行通知,并且正确的关闭了Thread,让应用程序能够正常关闭。但仔细思考加入Stop方法这件事,会发现这个Stop方法是用来管理ThreadSlave里Thread的生命周期,对于NormalSlave来说显得有点多余,并且这个职责也不是Master的职责。也就是说Stop方法违反了面向对象设计的精神,是由下层界面的「特定实做」来变更上层对象。

 

另外再从整个系统架构来说,为了加入这个Stop方法,必须要从ThreadSlave、NormalSlave、ISlave、Master等等一路往上去做这个修改。这在系统小的时候,靠开发人员的辛劳,可以完成这样的修改设计。但如果是一个庞大系统,系统里的对象,已经像是端午节吃不完的肉粽那么多,这个时候要来加入这个Stop方法的修改,除了劳民伤财之外,也很容易不小心改错而造成系统执行的错误。

 

 

 

using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;

namespace ConsoleApplication3
{
    class Program
    {
        static void Main(string[] args)
        {
            // Slave
            ISlave slave = new ThreadSlave();

            // Master
            Master master = new Master(slave);

            // Execute
            master.Execute();

            // Wait
            System.Threading.Thread.Sleep(5000);
            Console.WriteLine("End");

            // Stop
            master.Stop();
        }
    }

    public class Master
    {
        // Fields
        private readonly ISlave _slave = null;


        // Constructors
        public Master(ISlave slave)
        {
            #region Contracts

            if (slave == null) throw new ArgumentException();

            #endregion

            // Slave
            _slave = slave;
            _slave.MessageNotified += this.Slave_MessageNotified;
        }


        // Methods 
        public void Execute()
        {
            // Slave
            _slave.Execute();
        }

        public void Stop()
        {
            // Slave
            _slave.Stop();
        }


        // Handlers
        private void Slave_MessageNotified(string message)
        {
            #region Contracts

            if (string.IsNullOrEmpty(message) == true) throw new ArgumentException();

            #endregion

            // Print
            Console.WriteLine(string.Format("{0} {1}", DateTime.Now.ToString("HH:mm:ss"), message));
        }
    }

    public interface ISlave
    {
        // Methods 
        void Execute();

        void Stop();


        // Events
        event Action<string> MessageNotified;
    }

    public class ThreadSlave : ISlave
    {
        // Fields
        private readonly System.Threading.Thread _thread = null;


        // Constructors
        public ThreadSlave()
        {
            // Thread
            _thread = new System.Threading.Thread(this.Operation);
            _thread.Start();
        }

        // Methods 
        public void Execute()
        {
            this.OnMessageNotified("Work");
        }

        public void Stop()
        {
            _thread.Abort();
        }

        private void Operation()
        {
            while (true)
            {
                System.Threading.Thread.Sleep(1000);
                this.OnMessageNotified("Alive");
            }
        }


        // Events
        public event Action<string> MessageNotified;
        private void OnMessageNotified(string message)
        {
            #region Contracts

            if (string.IsNullOrEmpty(message) == true) throw new ArgumentException();

            #endregion

            var handler = this.MessageNotified;
            if (handler != null)
            {
                handler(message);
            }
        }
    }
}



加入Thread.IsBackground = true的机制

 

在.NET中,为了简化对于Thread这类资源的生命周期管理,为Thread类别加入了IsBackground属性。.NET的CLR会在应用程序前景线程结束的时候,去检查目前执行中的Thread,如果这个Thread的IsBackground设定为true,CLR就会主动去关闭这条Thread。藉由这样自动关闭的功能,就能减少一些开发人员管理Thread生命周期的工作。

 

将这个机制套用到ThreadSlave里,让ThreadSlave所开启的Thread,其生命周期交由CLR去管理。透过这样的方式,在ThreadSlave中就不需要加入Stop方法来关闭Thread,进而不用再去修改NormalSlave、ISlave、Master等等对象,也就才能真正的享用到套用IoC的好处。

 

 

 

using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;

namespace ConsoleApplication4
{
    class Program
    {
        static void Main(string[] args)
        {
            // Slave
            ISlave slave = new ThreadSlave();

            // Master
            Master master = new Master(slave);

            // Execute
            master.Execute();

            // Wait
            System.Threading.Thread.Sleep(5000);
            Console.WriteLine("End");
        }
    }

    public class Master
    {
        // Fields
        private readonly ISlave _slave = null;


        // Constructors
        public Master(ISlave slave)
        {
            #region Contracts

            if (slave == null) throw new ArgumentException();

            #endregion

            // Slave
            _slave = slave;
            _slave.MessageNotified += this.Slave_MessageNotified;
        }


        // Methods 
        public void Execute()
        {
            // Slave
            _slave.Execute();
        }


        // Handlers
        private void Slave_MessageNotified(string message)
        {
            #region Contracts

            if (string.IsNullOrEmpty(message) == true) throw new ArgumentException();

            #endregion

            // Print
            Console.WriteLine(string.Format("{0} {1}", DateTime.Now.ToString("HH:mm:ss"), message));
        }
    }

    public interface ISlave
    {
        // Methods 
        void Execute();


        // Events
        event Action<string> MessageNotified;
    }

    public class ThreadSlave : ISlave
    {
        // Fields
        private readonly System.Threading.Thread _thread = null;


        // Constructors
        public ThreadSlave()
        {
            // Thread
            _thread = new System.Threading.Thread(this.Operation);
            _thread.IsBackground = true;
            _thread.Start();
        }

        // Methods 
        public void Execute()
        {
            this.OnMessageNotified("Work");
        }

        private void Operation()
        {
            while (true)
            {
                System.Threading.Thread.Sleep(1000);
                this.OnMessageNotified("Alive");
            }
        }


        // Events
        public event Action<string> MessageNotified;
        private void OnMessageNotified(string message)
        {
            #region Contracts

            if (string.IsNullOrEmpty(message) == true) throw new ArgumentException();

            #endregion

            var handler = this.MessageNotified;
            if (handler != null)
            {
                handler(message);
            }
        }
    }
}



范例下载

 

DIThreadTips.rar




后记

 

这篇文章乍看之下,会觉得最终只有介绍Thread.IsBackground这个属性的功能。但主要是想透过这样一个小小的范例演化,让开发人员能够体验面向对象分析设计,过程中的一些考虑:不要由特定实做来变更上层对象的职责、资源生命周期的管理(例如Thread)…等等。希望能透过这样的文字说明,帮助到有需要的开发人员。:D


posted @ 2013-06-09 13:14  Clark159  阅读(225)  评论(0编辑  收藏  举报