一、线程同步,先来看一下一段代码:
Thread t1 = new Thread(new ThreadStart(() => { //1.递增 for (int i = 0; i < 10000000; i++) { _count++; //_count = _count + 1;//分为3个步骤,取到_count的值,把_count的值加1,把计算好的值重新赋值给_count } })); t1.Start(); Console.WriteLine("t1线程已经启动,开始对_count变量++"); Console.WriteLine("主线程继续执行....."); //2.递减 for (int i = 0; i < 10000000; i++) { _count--; } //到这里的时候要保证主线程的--操作与t1线程的++操作都执行完毕 //等待t1执行完毕 t1.Join(); Console.WriteLine("_count结果是:{0}", _count); Console.ReadKey();
结果是:
这个结果是不是很惊讶?因为_count++或者_count--分为上述三个步骤! 线程什么时候执行或者暂停是由cpu决定的,我们可能在准备++的时候拿到的_count的值是1000,
但是线程暂停了,去执行_count--,它那边拿到的值也是1000,线程可能又转去执行++完之后是1001,赋给_count,但是之后执行--是999,再次赋给_count,结果就不对了。
二、解决并发访问变量问题
static readonly object objSync = new object(); Thread t1 = new Thread(new ThreadStart(() => { //1.递增 for (int i = 0; i < max; i++) { lock (objSync)//向系统申请可不可以 锁定objSync对象 如果objSync对象没有被锁定,那么可以 如果objSync对象呗锁定了,那么这个语句会暂停,直到申请到mobjSync
//对象 { //在lock块中的代码同时只能有一个线程来访问。 _count++; }//释放第objSync的锁定 } })); t1.Start(); Console.WriteLine("t1线程已经启动,开始对_count变量++"); Console.WriteLine("主线程继续执行....."); //2.递减 //加锁后执行变慢!! for (int i = 0; i < max; i++) { lock (objSync) { _count--; } } //到这里的时候要保证主线程的--操作与t1线程的++操作都执行完毕 //等待t1执行完毕 t1.Join(); Console.WriteLine("_count结果是:{0}", _count); Console.ReadKey();
锁的本质是什么呢?我们反编译一下代码:
可以看到就是 Monitor.Enter与 Monitor.Exit,我们改造一下上面的代码:
Thread t1 = new Thread(new ThreadStart(() => { //1.递增 for (int i = 0; i < max; i++) { //lock (objSync) //{ // //在lock块中的代码同时只能有一个线程来访问。 // _count++; //} //标记当前是否已经上锁 bool isLockOk = false; //上锁 Monitor.Enter(objSync, ref isLockOk); try { _count++; } finally { if (isLockOk) { Monitor.Exit(objSync); } } } })); t1.Start(); Console.WriteLine("t1线程已经启动,开始对_count变量++"); Console.WriteLine("主线程继续执行....."); //2.递减 //加锁后执行变慢!! for (int i = 0; i < max; i++) { lock (objSync) { _count--; } } t1.Join(); Console.WriteLine("_count结果是:{0}", _count); Console.ReadKey();
锁住的对象为什么不能是值类型呢?
Monitor.Enter或者Monitor.Exit的参数是object类型,如果传进来一个值类型,就会装箱,产生一个新的对象!那么这两次操作的对象都不是同一个对象,不是同一个地址,
所以只能用引用类型来当做所变量。