混合线程同步构造:合并了用户模式和内核模式构造。没有线程竞争时,混合构造提供了基元用户模式构造所具备的性能优点。多个线程竞争一个构造时,混合构造经过基元内核模式的构造来提供不“自旋”的优点。数据结构
internal sealed class SimpleHybirdLock : IDisposable
{ private Int32 m_waiters = 0; private AutoResetEvent m_waiterLock = new AutoResetEvent(false); public void Enter() { if(Interlockd.Increment(ref m_waiters )== 1) return; m_waiterLock.Wati } public void Leave() { if(Interlickd.Decrement(ref m_waiters) == 0 ) return; m_waiterLcok.Set(); } public void Dispose() { m_waiterLock.Dispose(); } }
因为转换为内核模式会形成巨大的性能损失,并且线程占有锁的时间一般都很短,因此为了提高应用程序的整体性能,可让一个线程在用户模式中“自旋”一小段时间,再让线程转换为内核模式。若是线程正在等待的锁在线程“自旋”期间变得可用,就能避免向内核模式的转换了。此外,有的锁限制只能由得到所得线程释放锁。有的锁容许当前拥有它的线程递归地拥有锁。并发
internal public sealed class AnotherHybridLock : IDisposable
{ private Int32 m_waiters = 0;
private AutoResetEvent m_waiterLock = new AutoResetEvent(false);
private Int32 m_spincout = 4000;
private Int32 m_owingThreadId = 0, m_recursion = 0;
public void Enter()
{
Int32 threadId = Thread.CurrentThread.ManagedThreadId;
if(threadId == m_owingThreadId){
m_recursion++; return;
}
SpinWait spinwait = new SpinWait();
} }
30.3.1 ManualResetEventSlim类和SemaphoreSilm类异步
两个构造的工做方式和对应的内核模式构造彻底一致,只是它们都在用户模式中“自旋”,并且都推迟到发生第一次竞争时,才建立内核模式的构造。性能
30.3.2 Monitor类和同步块spa
堆中的每一个对象均可关联一个名为同步块的数据结构。同步块包含字段。它为内核对象,拥有线程的ID,递归计数以及等待线程计数提供了相应的字段。线程
问题总结:代理
变量能引用一个代理对象-前提是变量引用的那个对象的类型派生自System.MarshalByRefObject类。code
若是线程调用Monitor.Enter,向它传递对类型对象的引用,并且这个类型对象是以“AppDomain中立”的方式加载的,线程就会跨越进程中的全部AppDomain在那个类型上获取锁。对象
因为字符串能够留用,因此两个彻底独立的代码段可能在不知情的状况下获取对内存的一个String对象引用。blog
跨越AppDomain边界传递字符串时,CLR不建立字符串的副本;相反,它只是将对字符串的一个引用传给其余AppDomain。
因为Monitor的方法要获取一个Object,因此传递类型会致使类型被装箱,形成现场在以装箱对象上获取锁。
向方法应用【MethodImpl(MethodImplOption.Synchronized)】特性,会形成JIT编译器用Monitor.Enter和Monitor.Exit调用包围方法的本机代码。
调用类型的类型构造器时,CLR要获取类型对象上的一个锁,确保只有一个线程初始化类型对象及其静态字段。
30.3.3 ReaderWriterLockSilm类
ReaderWriterLockSilm:
一个线程向数据写入时,请求访问的其余全部线程都被阻塞。
一个线程从数据读取时,请求的读取的其余线程容许继续执行,但请求写入的线程仍被阻塞。
向线程写入的线程结束后,要么解除一个写入线程的阻塞,使它能向数据写入,要么解除全部读取线程的阻塞,使它能并发读取数据。若是没有线程被阻塞,锁就进入能够自由使用的状态,可供下一个reader或writer线程获取。
从数据读取的全部线程结束后,一个writer线程被解除阻塞,使它能向数据写入,若是没有线程被阻塞,锁就进入能够自由使用的状态,可供下一个reader或writer线程获取。
30.3.4 OneManyLock类
30.3.5 CountdownEvent类
这个构造使用一个ManualResetEventSlim对象。这个构造阻塞一个线程,知道它的内部计数变成0。和Semaphore的行为相反。
30.3.6 Barrier类
30.3.7 线程同步构造小结
代码尽可能不要阻塞任何线程。执行异步计算或I/O操做时,将数据从一个线程交给另外一个线程时,应避免多个线程同时访问数据。若是不能彻底作到这一点,请尽可能使用Volatile和Interlocked的方法。
如下两种状况阻塞线程。
线程模型很简单。
线程有专门用途。
开发人员用“双检锁”将单实例对象的构造推迟到应用程序首次请求该对象时进行。也称“延迟初始化”。