[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);
}
}
}
}
范例下载
后记
这篇文章乍看之下,会觉得最终只有介绍Thread.IsBackground这个属性的功能。但主要是想透过这样一个小小的范例演化,让开发人员能够体验面向对象分析设计,过程中的一些考虑:不要由特定实做来变更上层对象的职责、资源生命周期的管理(例如Thread)…等等。希望能透过这样的文字说明,帮助到有需要的开发人员。:D
期許自己~
能以更簡潔的文字與程式碼,傳達出程式設計背後的精神。
真正做到「以形寫神」的境界。









浙公网安备 33010602011771号