其它同步对象
"A statement is Atomic if it executes as a single indivisible instruction. Strict atomicity precludes any possible preemption. In C#, a simple read or assignment on a field of 32 bits or less is atomic (assuming a 32-bit CPU). Operations on larger fields are non-atomic, as are statements that combine more than one read/write operation."
--Threading in C#, Joseph Albahari
参考下述代码:
using System; using System.Threading; namespace AtomicTest { class AtomicTest { static int x, y; static long z; static coid Test() { long myVar; x = 3; //Atomic z = 3; //Non atomic as Z is 64 bits myVar = z; //Non atomic as Z is 64 bits y += x; //Non atomic read and write x++; //Non atomic read and write } } }
解决上述问题的一种方式是使用lock关键字。但是,.net其实提供了一种更简单也更快的方式,那就是Interlocked
。Interlocked
比lock更安全,因为Interlocked
不会阻塞。
MSDN :
"The methods of this class help protect against errors that can occur when the scheduler switches contexts while a thread is updating a variable that can be accessed by other threads, or when two threads are executing concurrently on separate processors. The members of this class do not throw exceptions."
下面是一个使用 Interlocked
类的示例:
using System; using System.Threading; namespace InterlockedTest { class Program { static long currentValue; static void Main(string[] args) { //simple increment/decrement operations Interlocked.Increment(ref currentValue); Console.WriteLine(String.Format( "The value of currentValue is {0}", Interlocked.Read(ref currentValue))); Interlocked.Decrement(ref currentValue); Console.WriteLine(String.Format( "The value of currentValue is {0}", Interlocked.Read(ref currentValue))); Interlocked.Add(ref currentValue, 5); Console.WriteLine(String.Format( "The value of currentValue is {0}", Interlocked.Read(ref currentValue))); //read a 64 bit value Console.WriteLine(String.Format( "The value of currentValue is {0}", Interlocked.Read(ref currentValue))); Console.ReadLine(); } } }
结果如下:
Interlocked
类还提供其它方法:
CompareExchange(location1,value,comparand)
: 如果comparand
和位于location1
的值相同,则将value存储于location1
。否则不做任何操作,CompareExchange
的返回值为location1
的原值,不论交换是否发生。Exchange(location1,value)
: 将value赋予位于location1的变量,同时返回原有的值,整个过程是个原子操作。
这两个函数可以代替Monitor或者Mutex等内核锁来实现lock-free(wait-free)算法和数据结构。
volatile
关键字表明被其修饰的变量可以为OS,硬件,或者并发的线程修改。
MSDN :
"The system always reads the current value of a volatile object at the point it is requested, even if the previous instruction asked for a value from the same object. Also, the value of the object is written immediately on assignment.
The
volatile
modifier is usually used for a field that is accessed by multiple threads without using thelock
statement to serialize access. Using thevolatile
modifier ensures that one thread retrieves the most up-to-date value written by another thread."
常见的情况是,对于某类型其读操作是线程安全的,但是更新操作却不是。尽管可以通过lock关键字来弥补,但是这样限制性又太强。ReaderWriterLockSlim
就适用于这种情况。
ReaderWriterLockSlim
class (.NET 3.5) 提供两种锁: read lock 和 write lock. write lock 是独占的, 而read lock则可以与其他read lock 兼容.
如果某个线程持有write lock,则将阻塞所有其它试图read lock 和 write lock的线程。如果没有线程锁定write lock,则任意数量的线程可以获取read lock。
ReaderWriterLockSlim
class提供的主要方法如下:
EnterReadLock
(*)ExitReadLock
EnterWriteLock
(*)ExitWriteLock
(*)示例(original example courtesy of Threading in C#, Joseph Albahari):
using System; using System; using System.Threading; using System.Collections.Generic; namespace ReaderWriterLockSlimTest { /// <summary> /// This simple class demonstrates the usage a Reader/Writer /// situation, using the ReaderWriterLockSlim class /// </summary> class Program { static ReaderWriterLockSlim rw = new ReaderWriterLockSlim(); static List<int> items = new List<int>(); static Random rand = new Random(); static void Main(string[] args) { //start some readers new Thread(Read).Start("R1"); new Thread(Read).Start("R2"); new Thread(Read).Start("R3"); //start some writers new Thread(Write).Start("W1"); new Thread(Write).Start("W2"); } static void Read(object threadID) { //do read while (true) { try { rw.EnterReadLock(); Console.WriteLine("Thread " + threadID + " reading common source"); foreach (int i in items) Thread.Sleep(10); } finally { rw.ExitReadLock(); } } } static void Write(object threadID) { //do write while (true) { int newNumber = GetRandom(100); try { rw.EnterWriteLock(); items.Add(newNumber); } finally { rw.ExitWriteLock(); Console.WriteLine("Thread " + threadID + " added " + newNumber); Thread.Sleep(100); } } } static int GetRandom(int max) { //lock on the Random object lock (rand) return rand.Next(max); } } }
结果如下:
在 System.Threading
域名下还有一个ReaderWriterLock
类。下面是 MSDN 中关于ReaderWriterLockSlim
(.NET 3.5)和ReaderWriterLock
(.NET 2.0)的区别:
ReaderWriterLockSlim
is similar toReaderWriterLock
, but it has simplified rules for recursion and for upgrading and downgrading lock state.ReaderWriterLockSlim
avoids many cases of potential deadlock. In addition, the performance ofReaderWriterLockSlim
is significantly better thanReaderWriterLock
.ReaderWriterLockSlim
is recommended for all new development.