一、线程同步,先来看一下一段代码:
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类型,如果传进来一个值类型,就会装箱,产生一个新的对象!那么这两次操作的对象都不是同一个对象,不是同一个地址,
所以只能用引用类型来当做所变量。