.NET进阶篇06-async异步、thread多线程4

知识需要不断积累、总结和沉淀,思考和写作是成长的催化剂

梯子

一、锁

数据库中也有锁概念,行锁,表锁,事物锁等,锁的作用就是控制并发情况下数据的安全一致,使一个数据被操作时,其他并发线程等待。开发方面多线程并行编程访问共享数据时,为保证数据的一致安全,有时需要使用锁来锁定对象来达到同步

.NET中提供很多线程同步技术。有lock,Interlocked,Monitor等用于进程内同步锁,Mutex互斥锁,Semaphore信号量,Events,ReaderWriterLockSlim读写锁等用于多个进程间的线程同步

1、lock

lock语句是设置对锁定和解除锁定的一种简单方式,也是最常用的一种同步方式。lock用于锁定一个引用类型字段,当线程执行到Lock处,会锁定该字段,使之只有一个线程进入lock语句块内,才lock语句结束位置再释放锁定,另一个线程才可以进入。原理运用同步块索引,感兴趣可以研究下

lock (obj)
{
    //synchronized region
}

因为只有一个线程可以进去,没有并发,所以牺牲了性能,所以要尽量缩小lock的范围,另一个建议是首选锁一个私有变量,也就是SyncRoot模式,声明一个syncRoot的私有object变量来进行锁定,而不是使用lock(this),因为外面调用者也可能锁定你这个对象的实例,但他并不知道你内部也使用了锁,所以容易造成死锁

private object syscRoot = new object();
public void DoThis()
{
    lock (syscRoot)
    {
        //同一个时间只有一个线程能到达这里
    }
}
2、Interlocked

InterLoacked用于将变量的一些简单操作原子化,也就是线程安全同步。我们常写的i++就不是线程安全的,从内存中取值然后+1然后放回内存中,过程中很可能被其他线程打断,比如在你+1后放回内存时,另一个线程已经先放回去了,也就不同步了。InerLocked类提供了以线程安全的方式递增、递减、交换、读取值的方法
比如以下代替lock的递增方式

int num = 0;
//lock (syscRoot)
//{
//    num++;
//}
num = Interlocked.Increment(ref num);
3、Monitor

上面lock就是Monitor的语法糖,通过编译器编译会生成Monitor的代码,像下面这样

lock (syscRoot)
{
    //synchronized region
}
//上面的lock锁等同于下面Monitor
Monitor.Enter(syscRoot);
try
{
    //synchronized region
}
finally
{
    Monitor.Exit(syscRoot);
}

Monitor不同于Lock就是它还可以设置超时时间,不会无限制的等待下去。

bool lockTaken = false;
Monitor.TryEnter(syscRoot,500,ref lockTaken);
if (lockTaken)
{
    try
    {
        //synchronized region
    }
    finally
    {
        Monitor.Exit(syscRoot);
    }
}
else
{
}
4、SpinLock

SpinLock自旋锁是一种用户模式锁。对了,插一嘴锁分为内核模式锁和用户模式锁,内核模式就是在系统级别让线程中断,收到信号时再切回来继续干活,用户模式就是通过一些cpu指定或则死循环让线程一直运行着直到可用。各有优缺点吧,内核Cpu资源利用率高,但切换损耗,用户模式就相反,如果锁定时间较长,就会白白循环等待,后面就有混合模式锁的出现了

如果有大量的锁定,且锁定时间非常短,SpinLock就很有用,用法和Monitor类似,Enter或TryEnter获取锁,Exit释放锁。IsHeld和IsHeldByCurrentThread指定它当前是否锁定

另外SpinLock是个结构类型,所以注意拷贝赋值时会创建全新副本问题。必要时可按引用来传递

5、Mutex

内容版权声明:除非注明,否则皆为本站原创文章。

转载注明出处:https://www.heiqu.com/zyjjgs.html