健谈的 Locker 模式






4.11/5 (5投票s)
一个帮助调试多线程应用程序的设计模式。
引言
最近我一直在阅读许多 Martin Fowler 的设计模式(以及许多其他设计模式),并开始欣赏它们。我意识到设计模式不一定需要很复杂才能学习、理解和应用。该模式只需要解决特定的问题情况即可。至少,该模式应该帮助开发人员创建灵活且易于修改的代码。
话虽如此,我还没有看到许多广泛的模式来更好地帮助开发人员调试他们的多线程程序。我们都同意调试多线程代码很容易变成一场噩梦。追踪当前正在等待锁的线程、当前正在执行的线程以及即将释放锁的线程可能很麻烦。
背景
我想开发一个简单的设计模式,可以使开发人员更容易进行多线程调试。在本文中,我介绍了健谈的锁设计模式。
详细说明
健谈的锁模式旨在将代码的锁定区域与代码的其余部分隔离。有一个进入、获取和释放锁的单点。从某种意义上说,你实际上是在包装该区域。通过隔离该区域,你可以在进入的任何线程的锁定和执行阶段的每个点编写日志记录和调试信息。
这是一个非常简单的类,演示了健谈的锁模式
public class TalkativeLocker<T>
{
private readonly static object toLock = new object();
public T EnterLock(IClient client, Func<T> function)
{
T result = default(T);
Console.WriteLine(client.Name + ":Waiting for lock");
Monitor.Enter(toLock);
try
{
Console.WriteLine(client.Name + ":AquiredLock");
result = function();
}
catch (Exception ex)
{
//
}
finally
{
Console.WriteLine(client.Name + ":About release lock");
Monitor.Exit(toLock);
}
return result;
}
}
类TalkativeLocker
包含一个仅用于锁定的私有对象:toLock
。请注意,它是静态的
。这意味着它将被共享。一次只有一个线程能够获取其上的锁。TalkativeLocker
还有一个 Func<T>
参数。此参数表示在获取锁时要调用的过程。开发人员可以使用他们想要的任何参数。我选择Func<T>
是为了简单起见。
IClient
接口参数表示将使用 TalkativeLocker
类的类。这是接口的代码
public interface IClient
{
string Name
{
get;
set;
}
}
接口目前只有一个属性:Name
。Name
属性由 TalkativeLocker
类使用,以显示开发人员可能认为适合使其多线程行为在调试时更容易的任何信息。如果存在多个客户端线程,则应将 Name
属性设置为唯一值:一个将向开发人员标识线程的值。请注意,TalkativeLocker
类使用 IClient
接口上的 Name
属性将日志记录信息写入控制台。
这是一个实现该接口的简单类
public class Client : IClient
{
private string name = String.Empty;
private readonly TalkativeLocker<int> talkativeLocker =
new TalkativeLocker<int>();
public Client(string name)
{
Name = name;
}
public string Name
{
get
{
return name;
}
set
{
name = value;
}
}
public int AddTwoNumbers()
{
return 1 + 2;
}
public void DoSomething()
{
int result = talkativeLocker.EnterLock(this, AddTwoNumbers);
}
}
以及一个演示健谈的锁设计模式的示例类
static void Main(string[] args)
{
Client[] clients = new Client[10];
clients[0] = new Client("1");
clients[1] = new Client("2");
clients[2] = new Client("3");
clients[3] = new Client("4");
clients[4] = new Client("5");
clients[5] = new Client("6");
clients[6] = new Client("7");
clients[7] = new Client("8");
clients[8] = new Client("9");
clients[9] = new Client("10");
foreach (Client client in clients)
{
Thread t = new Thread(client.DoSomething);
t.Start();
}
Thread.CurrentThread.Join();
Console.ReadLine();
}
请注意,我已经用它们自己的唯一 ID 初始化了每个客户端。
你的输出将如下所示
我现在可以看到每个线程在争夺锁、获取锁和最终释放锁时的状态。
如果您认为健谈的锁设计模式使您的多线程代码调试更容易,请告诉我。