• [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


  • 相关阅读:
    Mybatis配置文件
    maven添加镜像与常用配置
    互联网级微服务架构神器Duubo
    Mybatis入门
    SpringMVC文件下载与JSON格式
    SpringMVC拦截器与异常处理
    SpringMVC国际化与文件上传
    cinder
    horizon
    glance
  • 原文地址:https://www.cnblogs.com/clark159/p/3128746.html
Copyright © 2020-2023  润新知