本文档只是自己的学习笔记,可能有误,如果有误请大家帮忙指出,谢谢。
争用条件
当多个线程同时对一个对象进行操作时,可能就会出现争用条件的情况,这么说可能大家没法理解,直接上例子
例:
先声明一个对象,其中有一个int值默认为5,还有一个ChangeId方法,在这个方法中,会先对nameId进行自增操作,再判断如果nameId为5的话,就输出“nameId等于5”。
class Class1
{
private int nameId = 5;
public void ChangeId()
{
nameId++;
if (nameId == 5)
{
Console.WriteLine("nameId等于5");
}
nameId = 5;
}
}
我想很多人看到这都时候都会想,在这个方法中肯定不可能输出nameID等于5的语句。那么我们再接着往下看
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using System.Threading;
namespace 争用条件
{
class Program
{
static void changeID(object o)
{
Class1 class1 = o as Class1;
while (true)
{
class1.ChangeId();
}
}
static void Main(string[] args)
{
Class1 test = new Class1();//声明定义一个对象
Thread thread = new Thread(changeID);//开启一个线程,在线程中传入changeID方法
thread.Start(test);//开始线程
//new Thread(changeID).Start(test);
}
}
}
结果:
可以看到,当只有一个线程访问的时候,对象的方法中是不可能输出 nameI等于5的语句,但是如果有两个线程同时访问的时候回发生怎么样的情况呢?话不多说,看案例
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using System.Threading;
namespace 争用条件
{
class Program
{
static void changeID(object o)
{
Class1 class1 = o as Class1;
while (true)
{
class1.ChangeId();
}
}
static void Main(string[] args)
{
Class1 test = new Class1();
Thread thread = new Thread(changeID);
thread.Start(test);
//new Thread(changeID).Start(test);
Thread thread2 = new Thread(changeID);
thread2.Start(test);
}
}
}
结果:
可以看到,语句不停的在输出nameId等于5,这是为什么呢?
这时候,再让我们好好看看Class1类
class Class1
{
private int nameId = 5;
public void ChangeId()
{
nameId++;
if (nameId == 5)
{
Console.WriteLine("nameId等于5");
}
nameId = 5;
}
}
我们开启了两个线程同时调用同一个对象的ChangeId方法,当线程一运行到if(name==5),线程二运行到nameId.==5的时候,因为两个线程修改的是同一个对象的同一个nameId,所以就会导致nameId等于5的情况成立,就会满足if条件不停输出 “name等于5”的语句。这个就称为争用条件,那我们要怎么解决这个问题呢?
在这里,我们可以使用lock(锁)解决争用条件的问题
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using System.Threading;
namespace 争用条件
{
class Program
{
static void changeID(object o)
{
Class1 class1 = o as Class1;
while (true)
{
lock (class1)//向系统申请锁定class1对象,如果class1已经被其他线程锁定,则等待class1对象解锁,再在该线程锁定class1对象,若class1对象未被锁定,则可以直接在该线程将其锁定
{
class1.ChangeId();//在同一个时刻,只有一个线程在执行该方法
}//当lock结束,则class1对象解锁
}
}
static void Main(string[] args)
{
Class1 test = new Class1();
Thread thread = new Thread(changeID);
thread.Start(test);
//new Thread(changeID).Start(test);
Thread thread2 = new Thread(changeID);
thread2.Start(test);
}
}
}
结果:
可以看到现在就输出不了nameId等于5的语句。
但是,如果使用lock不当,会出现死锁情况
死锁例子:
public void Deadlock1(){
int i =0;
while(true){
lock(s1){
lock(s2){
s1.ChangeState(i);
s2.ChangeState(i);
i++;
Console.WriteLine("Running i : "+i);
}
}
}
}
public void Deadlock2(){
int i =0;
while(true){
lock(s2){
lock(s1){
s1.ChangeState(i);
s2.ChangeState(i);
i++;
Console.WriteLine("Running i : "+i);
}
}
}
}
}
解析:
如果我们线程一现在已经锁定s1等待s2解锁,而线程二现在已经锁定s2等待线程一解锁,这种情况下,线程一永远等不到s2解锁,而且线程二永远等不到线程一解锁,这就出现了死锁情况
那么我们如何解决死锁情况呢
解决:在编程的开始设计阶段,设计锁定顺序
当线程一先锁定s1再锁定s2的时候,线程二也要使用这个锁定顺序,这样就不会出现死锁情况