同步的目的是防止多个线程访问共享数据的破坏问题,关键点:
处理同步是容易出错的
通过锁对性能会有影响
同一个时刻只允许一个线程写共享资源
一些建议
避免静态字段共享数据(只读除外)
创建的对象在线程之间不传递使用,也不需要同步
最好使用值类型,线程操作的是这些类型的拷贝,因此不需要同步
用户模式、内核模式基元构造
优点 |
缺点 |
例子 |
|
基元用户模式 |
使用特殊的CPU指令,速度比内核模式快 |
OS不能侦测到线程的阻塞,会多次反复调度 |
易失 Volatile construct:Thread.VolatileWrite Thread.VolatileRead volatile 互锁构造 Interlocked construct System.Threading.Interlocked [都要求传递对“包含一个简单数据类型的一个变量”的引用] |
基元内核模式 |
Windows系统自身提供的 会阻塞线程,不会浪费CPU |
用户态切换到内核态有性能损失 |
事件、信号量(以上两者基础上的如互斥体) 事件是内核维护的Boolean变量AutoResetEvent 信号量是由内核维护的Int32变量 互斥体和AutoResetEvent(或计数为1的Semaphore)相似 ThreadPool.RegisterWaitForSingleObject线程池等待内核对象进入可用状态,更高效的方式 |
混合线程同步构造:一般情况下,通过合并用户模式和内核模式同步构造,没有线程竞争时,提供了内核模式的性能;竞争时提供了内核模式的优点。
System.Threading下的混合线程同步构造
ManualResetEventSlim、SemaphoreSlim、ReaderWriterLockSlim特别处在于它们都在用户模式中“自旋”,而且都推迟到发生第一次竞争时,才创建内核模式的构造
Monitor的使用:
每个对象都使用私有的锁
///防止反复测试条件的浪费CPU的一个方法
public sealed class SynchronizedQueue<T> {
private readonly Object m_lock = new Object();
private readonly Queue<T> m_queue = new Queue<T>();
public void Enqueue(T item) {
Monitor.Enter(m_lock);
m_queue.Enqueue(item);
Monitor.PulseAll(m_lock); // Wakeup any/all waiters 激发
Monitor.Exit(m_lock);
}
///在有一个可供处理的数据项之前,试图出队一个数据项的线程会一直阻塞
public T Dequeue() {
Monitor.Enter(m_lock);
// Loop waiting for condition (queue not empty)
while (m_queue.Count == 0)
Monitor.Wait(m_queue);//释放锁所有权,等待激发条件
T item = m_queue.Dequeue();
Monitor.Exit(m_lock);
return item;
}
}
单例模式和Lazy中使用的锁
public Lazy(LazyThreadSafetyMode mode)
LazyInitializer.EnsureInitialized
把对象的初始化放到需要的时刻,以减少内容的占用
更详细的锁的使用和处理参考:
图书 Clr Via C#
图书资源的网站 http://transbot.blog.163.com