锁的内容很重要,首先必须明确,只有用到使用相同变量时,会发生竞争。如果线程操作的是不同实例,那么不会发生影响。
以下代码先构造一个抽象类CounterBase,里面说明所有其子类必须Increment()和Decrement()两个方法。其子类有两个:Counter和CounterWithLock。比较有意思的是两者都用的是Count属性(private set),估计是可以生成默认的内部变量。前者不管锁,直接自增自减;后者有lock,在执行Increment()时(使用到了Count),那么该实例不能同时使用Decrement,必须等到Increment执行完才可以。需要指出的是锁定的是个对象object_synroot,而值类型不能作为锁定对象。还有,锁定对象是唯一的、确定的。
TestCounter是通过传入两个类型的实例来查看结果。
static void TestCounter(CounterBase c)
{
for (int i = 0; i < 100000; i++)
{
c.Increment();
c.Decrement();
}
}
class Counter : CounterBase
{
public int Count { get; private set; } //外部无法set该属性
public override void Increment()
{
Count++;
}
public override void Decrement()
{
Count--;
}
}
class CounterWithLock : CounterBase
{
private readonly object _syncRoot = new Object();
public int Count { get; private set; }
public override void Increment()
{
lock (_syncRoot)
{
Count++;
}
}
public override void Decrement()
{
lock (_syncRoot)
{
Count--;
}
}
}
abstract class CounterBase
{
public abstract void Increment();
public abstract void Decrement();
}
main函数:
Console.WriteLine("Incorrect counter");
var c = new Counter();
var c_1 = new Counter();
var t1 = new Thread(() => TestCounter(c));
var t2 = new Thread(() => TestCounter(c_1));
t1.Start();
t2.Start();
t1.Join();
t2.Join();
Console.WriteLine("Total count: {0}",c.Count);
Console.WriteLine("Total count: {0}",c_1.Count);
这个实例是为了说明如果不同实例,是不需要加锁的。
调用含有锁的对象:
Console.WriteLine("Correct counter");
var c1 = new CounterWithLock();
t1 = new Thread(() => TestCounter(c1));
t2 = new Thread(() => TestCounter(c1));
t3 = new Thread(() => TestCounter(c1));
t1.Start();
t2.Start();
t3.Start();
t1.Join();
t2.Join();
t3.Join();
Console.WriteLine("Total count: {0}", c1.Count);
Console.Read();
注意这里用的都是同一实例c1,这样才能体现锁的价值。