线程同步:
1.为什么进行线程的同步:
首先我们在进行对于全局变量的写操作的时候如果不进行线程同步的话就会造成竞态条件使得数据的完整性的破坏
举例说明:
访问全局变量的时候没有进行线程同步:
const int _Total = int.MaxValue;
static long _count = 0;
static void Main(string[] args)
{
Task task = Task.Run(() => decr());
for (int i = 0; i < _Total; i++) {
_count++;
}
task.Wait();
Console.WriteLine("count={0}",_count);
}
static void decr() {
for (int i = 0; i < _Total; i++) {
_count--;
}
}
输出结果按理来说应该是0,但是我们运行的时候结果却是一些奇奇怪怪的数字,造成这样的情况的根本原因就是数据的完整性被破坏了。
使用Mointer进行同步:
const int _Total = int.MaxValue;
static long _count = 0;
readonly static object _Sync = new object();
static void Main(string[] args)
{
Task task = Task.Run(() => decr());
for (int i = 0; i < _Total; i++) {
bool LockTaken = false; //标记
try
{
Monitor.Enter(_Sync, ref LockTaken);
_count++;
}
finally {
Monitor.Exit(_Sync);
}
}
task.Wait();
Console.WriteLine("count={0}",_count);
}
static void decr() {
for (int i = 0; i < _Total; i++) {
bool LockTaken = false; //标记
try
{
Monitor.Enter(_Sync, ref LockTaken);
_count--;
}
finally
{
Monitor.Exit(_Sync);
}
}
}
//输出:0
可见通过上面这种方式可以实现同步
原理:我们在使用Mointor对象的时候需要传一个object参数也就是说任何对象都可以当一个锁对象,首先我们的对象是由,对象头,实例属性,对其填充,三部分组成的,同时我们的每一个对象都有一个监视器对象,这个监视器对象主要的作用就是记录该对象的状态,其实里面最重要的就是一个几个,记录了等待当前对象锁的一个等锁池,因为对象的锁每次只能有一个线程持有,所以没有抢到锁的线程就会在这里等待。同时对象的头部信息中记录了对象的锁的状态。
Lock锁实现同步
const int _Total = int.MaxValue;
static long _count = 0;
readonly static object _Sync = new object();
static void Main(string[] args)
{
Task task = Task.Run(() => decr());
for (int i = 0; i < _Total; i++) {
lock(_Sync)
{
_count++;
}
}
task.Wait();
Console.WriteLine("count={0}",_count);
}
static void decr() {
for (int i = 0; i < _Total; i++) {
bool LockTaken = false; //标记
lock (_Sync)
{
_count--;
}
}
}
//输出:0
注意点:
1.同步对象千万不能是值类型,因为值类型在进行传递的时候会在线程本地创建一个副本,也就是说变味了局部变量,那这个锁也就没有意义了,如果一定要值类型的活,需要使用他的装箱类型。
2.避免使用this,typeof,string来当作锁对象。