Lock 语句:
Lock是C#中的关键字,他要锁定一个资源,lock的特点是:同时只能有一个线程进入lock的对象的范围,其他lock的线程就要等。Lock语句时设定锁定和解除锁定的一种简单方式。
注意:lock要锁定同一个对象,而且必须是引用类型的对象
private static int countec = 0;
private static object obj = new object();
static void Main(string[] args)
{
Thread t1 = new Thread(() =>
{
for (int i = 0; i < 1000; i++)
{
lock(obj) //注意lock要锁定同一个对象,而且必须是引用类型的对象
{
countec++; //自增1000次
}
Thread.Sleep(1);
}
});
t1.Start();
Thread t2 = new Thread(() =>
{
for (int i = 0; i < 1000; i++)
{
lock (obj)//注意lock要锁定同一个对象,而且必须是引用类型的对象
{
countec++; //自增1000次
}
Thread.Sleep(1);
}
});
t2.Start();
t1.Join();
t2.Join();
Console.WriteLine(countec); //输出2000
/*
* 改用lock解决多个线程同时操作一个资源。lock是C#中的关键字,他要锁定一个资源,lock的特点是:同时只能有一个线程进入lock的对象的范围,其他lock的线程就要等。
*/
Console.ReadKey();
}
输出结果:
注意:在一个地方使用Lock语句并不意味者,访问对象的其他线程都正在等待。必须对每个访问共享状态的线程显式地使用同步功能。
死锁:
过多的锁定也会有麻烦,容易产生死锁。在死锁中至少有两个线程被挂起,并等待对方解除锁定。由于两个线程都在 等待对方解除锁定,就会出现死锁现象,程序将无限等待下去。
例:
public class StateObject
{
private int state = 10;
private object locker = new object();
public void ChangeState(int loop)
{
lock(locker)
{
if(state==10)
{
state++;
Trace.Assert(state == 11, "竞态条件发生在" + loop + "loops");
}
state = 10;
}
}
}
public class SampleTack
{
private StateObject state1;
private StateObject state2;
public SampleTack(StateObject s1,StateObject s2)
{
this.state1 = s1;
this.state2 = s2;
}
public void Deadlock1()
{
int i = 0;
while(true)
{
lock(state1)
{
lock(state2)
{
state1.ChangeState(i);
state2.ChangeState(i++);
Console.WriteLine("仍在运行:{0}",i);
}
}
}
}
public void Deadlock2()
{
int i = 0;
while (true)
{
lock (state2)
{
lock (state1)
{
state1.ChangeState(i);
state2.ChangeState(i++);
Console.WriteLine("仍在运行:{0}", i);
}
}
}
}
}
调用:
static void Main(string[] args)
{
var st1 = new StateObject();
var st2 = new StateObject();
new Task(new SampleTack(st1, st2).Deadlock1).Start();
new Task(new SampleTack(st1, st2).Deadlock2).Start();
Console.ReadKey();
}
结果程序只在控制台输出1次,就没有响应了。
调试模式打开,选择 “全部中断” ,选择 “窗口” 点击 “任务”。
可是看到是锁死状态。
有时候死锁并不像上面例子那么明显。一个线程锁定了 st1,接着锁定了 st2;另一个线程锁定了st2,接着锁定了st1。在本例中只要改变锁定的顺序,这两个线程就会以相同的顺序进行锁定。但是,锁定可能隐藏在方法的深处。为了避免这个问题,可以在程序的体系架构中,从一开始就设计好锁定顺序,也可以为锁定定义超时时间。
方法上标注[MethodImpl(MethodImplOptions.Synchronized)],这样一个方法只能同时被一个线程访问。