微软的net framework类库保证了所有的静态方法是线程安全的,这意味着如果有2个线程同时调用一个静态方法,数据也不会出错。Framework类库必须这样设计,因为不可能让多个公司提供的程序集通过一个锁申请资源。Console类包含一个静态字段,Console很多方法通过内部对它的申请或者释放来保证同一时间只有一个线程访问console。
该线程即使不加锁,也是线程安全的,因为Int32是一个值类型,两个Int32类型的值会被复制后传入Max方法,因此多个线程可以同时调用Max方法,每个线程只方法自己的数据,和别的线程互相隔离。
另一方面,FCL不保证实例方法线程安全,因为为所有的代码加锁为损害性能。事实上,如果每个实例方法需要获取锁和释放锁,那么最终你的应用程序在任何时间都只有一个线程在运行,这更加损害了性能。当一个线程构建一个对象,只有该线程能引用这个对象,其他线程都不能访问该对象,调用该实例方法的时候也不需要线程同步。然而,如果线程把对象放置在静态区,暴露了对象引用,作为参数传到ThreadPool.QueueUserWorkItem或者Task等等,并且这个对象可以同时被读写访问,那么就需要线程同步。
建议你自己的类库也遵循这种模式,让你所有的静态方法线程安全,不需要让你所有的实例方法线程安全。这种模式有一个注意的地方,如果实例方法的目的是协调线程,那么该实例方法需要线程安全。例如,一个线程能通过调用CancellationTokenSource的Cancel方法取消一个操作,另一个线程通过询问负责协调的CancellationToken类的IsCancellationRequested属性来侦测线程是否应该停止。这两个实例成员有一些特殊的线程同步代码来保证这两个线程的协调。
有两种线程同步结构可以直接在你的代码中使用:用户模式和内核模式。尽可能的使用用户模式结构,因为这种结构明显的比内核模式结构快,因为这种结构使用了专用的CPU指令来协调线程,这就意味着在硬件级别协调线程。这也意味使用用户模式结构,Windows操作系统无法侦测到线程阻塞。
由于线程池线程在用户模式结构上的阻塞不被认为是阻塞,线程池也不会创建新的线程来替换这个临时被阻塞的线程,此外,这些CPU指令阻塞线程的时间极其短暂。听起来都不错吧?但是这也有一些负面的因素,只有Windows操作系统能内核可以停止一个线程,让它不用浪费CPU时间。用户模式下的线程可以被系统优先占取,但是此线程也会被再次调度。所以一个线程想要获得某些资源但是不能获得,将会空转,这将潜在的浪费CPU时间。