[Architecture Design] DI Thread Tips
套用IoC模式
在设计系统对象的时候,可以套用IoC模式来切割相依性。如下列范例程序代码,就是在Master、Slave两个对象之间套用IoC的小小范例,在这个范例中NormalSlave会透过MessageNotified事件,来将执行讯息通知给Master。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 | 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会持续的执行下去,而造成应用程序无法关闭。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 | 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方法的修改,除了劳民伤财之外,也很容易不小心改错而造成系统执行的错误。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 | 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的好处。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 | 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); } } } } |
范例下载
后记
这篇文章乍看之下,会觉得最终只有介绍Thread.IsBackground这个属性的功能。但主要是想透过这样一个小小的范例演化,让开发人员能够体验面向对象分析设计,过程中的一些考虑:不要由特定实做来变更上层对象的职责、资源生命周期的管理(例如Thread)…等等。希望能透过这样的文字说明,帮助到有需要的开发人员。:D
期許自己~
能以更簡潔的文字與程式碼,傳達出程式設計背後的精神。
真正做到「以形寫神」的境界。
【推荐】国内首个AI IDE,深度理解中文开发场景,立即下载体验Trae
【推荐】编程新体验,更懂你的AI,立即体验豆包MarsCode编程助手
【推荐】抖音旗下AI助手豆包,你的智能百科全书,全免费不限次数
【推荐】轻量又高性能的 SSH 工具 IShell:AI 加持,快人一步
· go语言实现终端里的倒计时
· 如何编写易于单元测试的代码
· 10年+ .NET Coder 心语,封装的思维:从隐藏、稳定开始理解其本质意义
· .NET Core 中如何实现缓存的预热?
· 从 HTTP 原因短语缺失研究 HTTP/2 和 HTTP/3 的设计差异
· 周边上新:园子的第一款马克杯温暖上架
· Open-Sora 2.0 重磅开源!
· 分享 3 个 .NET 开源的文件压缩处理库,助力快速实现文件压缩解压功能!
· Ollama——大语言模型本地部署的极速利器
· DeepSeek如何颠覆传统软件测试?测试工程师会被淘汰吗?