转自: https://www.cnblogs.com/lxblog/archive/2013/01/05/2846026.html
本篇我们主要总结和介绍一下利用属性标签方式对多线程进行方法同步和上下文同步,主要用到的是MethodImplAttribute 类 和 SynchronizationAttribute 类。
这两个属于方法特性和类的特性,标识某个方法或类是同步方法,本质上还是基于Lock的实现的。
首先我们还是来看一个例子,假如小明和小红两个人用都是主附银行卡,两个人都可以对帐户进行操作,比如帐户余额1000元,比如两个人几乎同时进行取钱600元的操作。
代码如下:我们没有进行任何的同步措施,我们运行一下代码,看一下结果:(不同计算机执行结果可能不同)
复制代码
namespace ThreadAttribute
{
class Program
{
static void Main(string[] args)
{
Account account = new Account();
Thread thread1 = new Thread(new ParameterizedThreadStart(account.WithDraw));
thread1.Name = "小明";
Thread thread2 = new Thread(new ParameterizedThreadStart(account.WithDraw));
thread2.Name = "小红";
thread1.Start(600);
thread2.Start(600);
Console.ReadKey();
}
}
public class Account
{
private static int _balance;
public int Blance
{
get
{
return _balance;
}
}
public Account()
{
_balance = 1000;
}
public void WithDraw(object money)
{
if ((int)money <= _balance)
{
Thread.Sleep(2000);
_balance = _balance - (int)money;
Console.WriteLine("{0} 取钱成功!余额={1}", Thread.CurrentThread.Name, _balance);
}
else
{
Console.WriteLine("{0} 取钱失败!余额不足!", Thread.CurrentThread.Name);
}
}
}
}
这结果显然是不对的,我们来对程序进行修改。
1. 我们先对MethodImplAttribute 类进行简单介绍:
在使用 MethodImplAttribute 类之前需要引用 System.Runtime.Remoting.Contexts (应该是System.Runtime.CompilerServices)命名空间,System.Runtime.Remoting.Contexts 命名空间 (和System.Runtime.CompilerServices命名空间) 包含的一些属性将影响CLR 在运行期间的行为,MethodImplAttribute 就是这样一个属性,MethodImplAttribute类的一个构造函数把MethodImplOptions枚举作为其参数,MethodImplOptions 枚举有一个字段Synchronized,,它指定在任一时刻只允许一个线程访问这个方法。
既然这样,我们对程序这样修改,直接在 WithDraw() 方法增加 MethodImplAttribute 标签,代码如下:
[MethodImplAttribute(MethodImplOptions.Synchronized)]
public void WithDraw(object money)
{
if ((int)money <= _balance)
{
Thread.Sleep(2000);
_balance = _balance - (int)money;
Console.WriteLine("{0} 取钱成功!余额={1}", Thread.CurrentThread.Name, _balance);
}
else
{
Console.WriteLine("{0} 取钱失败!余额不足!", Thread.CurrentThread.Name);
}
}
我们再次运行程序,结果如下图:
上面已经提到,这个两个类其实都是基于Lock关键字实现的,上面的代码就和下面直接用Lock 是一样的。
public void WithDraw(object money)
{
lock (this)
{
if ((int)money <= _balance)
{
Thread.Sleep(2000);
_balance = _balance - (int)money;
Console.WriteLine("{0} 取钱成功!余额={1}", Thread.CurrentThread.Name, _balance);
}
else
{
Console.WriteLine("{0} 取钱失败!余额不足!", Thread.CurrentThread.Name);
}
}
}
需要注意的是:
[MethodImplAttribute(MethodImplOptions.Synchronized)]标签应用到实例方法,相当于对当前实例加锁 lock(this)
[MethodImplAttribute(MethodImplOptions.Synchronized)]标签应用到静态方法,相当于当前类型加锁。如 WithDraw 是静态方法,就相当于 lock (typeof(Account))
2. 接下来我们再来看看SynchronizationAttribute类:
MSDN对SynchronizationAttribute的解释为:为当前上下文和所有共享同一实例的上下文强制一个同步域。
SynchronizationAttribute 的类:一个在 System.Runtime.Remoting.Contexts 命名空间中,另一个在 System.EnterpriseServices 命名空间中。System.EnterpriseServices.SynchronizationAttribute 类仅支持同步调用,并且只可与接受服务的组件一起使用。System.Runtime.Remoting.Contexts.SynchronizationAttribute 同时支持同步调用和异步调用,并且只可与上下文绑定对象一起使用。
至于什么应用程序域,什么是上下文,请看这两篇文章
1、进程、应用程序域、程序集、对象上下文
2、细说进程、应用程序域与上下文之间的关系
既然是SynchronizationAttribute 是类标签属性,那么我们需要对程序这样修改:
首先注释掉我们代码中的 [MethodImplAttribute(MethodImplOptions.Synchronized)] 方法标签并引用System.Runtime.Remoting.Contexts 命名空间,然后使 Account 类继承 ContextBoundObject,并为其增加 SynchronizationAttribute标签属性[Synchronization(SynchronizationAttribute.REQUIRED, true)]即可,代码如下:
[Synchronization(SynchronizationAttribute.REQUIRED, true)]
public class Account : ContextBoundObject
{
private static int _balance;
public int Blance
{
get
{
return _balance;
}
}
public Account()
{
_balance = 1000;
}
public void WithDraw(object money)
{
if ((int)money <= _balance)
{
Thread.Sleep(2000);
_balance = _balance - (int)money;
Console.WriteLine("{0} 取钱成功!余额={1}", Thread.CurrentThread.Name, _balance);
}
else
{
Console.WriteLine("{0} 取钱失败!余额不足!", Thread.CurrentThread.Name);
}
}
}
执行结果如下:
-= 本 文 源 码 下 载 =-
作者:Rising Sun
出处:http://www.cnblogs.com/lxblog/