
如:

Code
publicclassA
{
privatevolatileint_i;
publicintI
{
get{return_i; }
set{ _i=value; }
}
}

但volatile并不能实现真正的同步,因为它的操作级别只停留在变量级别,而不是原子级别。如果是在单处理器系统中,是没有任何弊端的,变量在主存中没有机会被其他人修改,因为唯有一个处理器,这就叫作processor Self-Consistency。但在多处理器系统中,可能还会有问题。每个处理器都有自己的data cach,而且被升级的数据也不必定会立刻写回到主存。所以可能会造成不同步c# 多线程 syncroot,但这些状况很难发生,因为cach的读写速度非常快,flush的频率也非常高,只有在压力测试的之后才有也许出现,而且概率比较特别小。
二、lock关键字
lock是一种比较好用的简洁的线程同步方式,它是借助为给定对象获得互斥锁来推动同步的。它可以确保当一个线程在关键代码段的之后,另一个线程不会进来,它只好期待,等到那种线程对象被释放,也就是说线程出了临界区。用法:

Code
publicvoidFunction()
{
objectlockThis=newobject();
lock(lockThis)
{
//Access thread-sensitive resources.
}
}

lock的参数需要是基于引用类型的对象,不要是基本类别像bool,int什么的,这样根本不能同步,原因是lock的参数规定是对象,如果传入int,势必要出现装箱操作,这样每天lock的都将是一个新的不同的对象。最好避免使用public类型或不受程序控制的对象例子,因为这种很也许避免死锁。特别是不要使用字符串作为lock的参数,因为字符串被CLR“暂留”,就是说整个应用程序中给定的字符串都只有一个实例,因此更容易造成死锁现象。建议使用不被“暂留”的私有或受保护成员成为参数。其实这些类尚未提供了专门用于被锁的成员,比如Array类型提供SyncRoot,许多其他集合类型也都提供了SyncRoot。
所以,使用lock应当留意下面几点:
1、如果一个类的例子是public的,最好不要lock(this)。因为使用你的类的人可能不知道你用了lock,如果他new了一个实例,并且对这个例子上锁,就很容易造成死锁。
2、如果MyType是public的,不要lock(typeof(MyType))
3、永远也不要lock一个字符串
三、System.Threading.Interlocked
对于整数数据类别的简单操作,可以用Interlocked类的成员来推动线程同步,存在于System.Threading命名空间。Interlocked类有下列步骤:Increment,Decrement,Exchange和CompareExchange。使用Increment和Decrement可以确保对一个整数的加减为一个原子操作。Exchange方法手动交换指定函数的值。CompareExchange方法组合了两个操作:比较两个值或者按照相当的结果将第三个值存储在其中一个变量中。比较和交换操作也有按原子操作执行的。如:

Code
inti=0;
System.Threading.Interlocked.Increment(refi);
Console.WriteLine(i);
System.Threading.Interlocked.Decrement(refi);
Console.WriteLine(i);
System.Threading.Interlocked.Exchange(refi,100);
Console.WriteLine(i);
System.Threading.Interlocked.CompareExchange(refi,10,100);

Output:


四、Monitor
Monitor类提供了与lock类似的功能,不过与lock不同的是,它能更好的控制同步块,当调用了Monitor的Enter(Object o)方法时,会获得o的独占权,直到调用Exit(Object o)方法时,才会释放对o的独占权,可以多次调用Enter(Object o)方法,只应该调用相同数量的Exit(Object o)方法就能,Monitor类同时提供了TryEnter(Object o,[int])的一个重载方式,该办法尝试获得o对象的独占权,当获得独占权成功时,将返回false。
但使用lock通常比直接使用Monitor更可取,一方面是因为lock更简单,另一方面是因为lock确保了但是受保护的代码导致异常,也可以释放基础监视器。这是借助finally中调用Exit来实现的。事实上,lock就是用Monitor类来实现的。下面两段代码是等效的:

Code
lock(x)
{
DoSomething();
}
等效于objectobj=(object)x;
System.Threading.Monitor.Enter(obj);
try
{
DoSomething();
}
finally
{
System.Threading.Monitor.Exit(obj);
}

关于用法,请参考以下的代码:

Code
privatestaticobjectm_monitorObject=newobject();
[STAThread]
staticvoidMain(string[] args)
{
Thread thread=newThread(newThreadStart(Do));
thread.Name="Thread1";
Thread thread2=newThread(newThreadStart(Do));
thread2.Name="Thread2";
thread.Start();
thread2.Start();
thread.Join();
thread2.Join();
Console.Read();
}
staticvoidDo()
{
if(!Monitor.TryEnter(m_monitorObject))
{
Console.WriteLine("Can't visit Object"+Thread.CurrentThread.Name);
return;
}
try
{
Monitor.Enter(m_monitorObject);
Console.WriteLine("Enter Monitor"+Thread.CurrentThread.Name);

Thread.Sleep(5000);
}
finally
{
Monitor.Exit(m_monitorObject);
}
}

当线程1获取了m_monitorObject对象独占权时,线程2尝试读取TryEnter(m_monitorObject),此时会因为能够获得独占权而返回false,输出信息如下:

另外,Monitor还提供了三个静态方法Monitor.Pulse(Object o),Monitor.PulseAll(Object o)和Monitor.Wait(Object o ) ,用来推动一种唤醒模式的同步。关于这三个方法的用法,可以参考MSDN,这里就不详述了。
五、Mutex
在使用上,Mutex与上述的Monitor比较接近,不过Mutex不具备Wait,Pulse,PulseAll的功能,因此,我们不能使用Mutex实现类似的激活的功能。不过Mutex有一个比较大的特征,Mutex是跨进程的,因此我们可以在同一台机器或者远程的机器上的多个进程上使用同一个互斥体。尽管Mutex也可以推动进程内的线程同步,而且功能也更强大,但这些状况下,还是推荐使用Monitor,因为Mutex类是win32封装的,所以它所必须的互操作转换更耗资源。
六、ReaderWriterLock
在考虑资源访问的之后,惯性上我们会对资源实行lock模式,但是在这种状况下,我们只是应该读取资源的数据,而不是修改资源的数据,在这些状况下获得资源的独占权无疑会妨碍运行效益,因此.Net提供了一种机制,使用ReaderWriterLock进行资源访问时,如果在某一时刻资源并没有获取写的独占权,那么可以获得多个读的访问权,单个写入的独占权,如果某一时刻终于获得了写入的独占权,那么其他调用的访问权应该进行期待,参考下面代码:

Code
privatestaticReaderWriterLock m_readerWriterLock=newReaderWriterLock();
privatestaticintm_int=0;
[STAThread]
staticvoidMain(string[] args)
{
Thread readThread=newThread(newThreadStart(Read));
readThread.Name="ReadThread1";
Thread readThread2=newThread(newThreadStart(Read));
readThread2.Name="ReadThread2";
Thread writeThread=newThread(newThreadStart(Writer));
writeThread.Name="WriterThread";
readThread.Start();
readThread2.Start();
writeThread.Start();
readThread.Join();
readThread2.Join();
writeThread.Join();
Console.ReadLine();
}
privatestaticvoidRead()
{
while(true)
{
Console.WriteLine("ThreadName"+Thread.CurrentThread.Name+"AcquireReaderLock");
m_readerWriterLock.AcquireReaderLock(10000);
Console.WriteLine(String.Format("ThreadName : {0} m_int : {1}", Thread.CurrentThread.Name, m_int));
m_readerWriterLock.ReleaseReaderLock();
}
}
privatestaticvoidWriter()
{
while(true)

{
Console.WriteLine("ThreadName"+Thread.CurrentThread.Name+"AcquireWriterLock");
m_readerWriterLock.AcquireWriterLock(1000);
Interlocked.Increment(refm_int);
Thread.Sleep(5000);
m_readerWriterLock.ReleaseWriterLock();
Console.WriteLine("ThreadName"+Thread.CurrentThread.Name+"ReleaseWriterLock");
}
}

在程序中,我们开启两个线程获取m_int的调用访问权,使用一个线程获得m_int的读取独占权,执行代码后,输出如下:

可以发现,当WriterThread获取到写入独占权后,任何其他调用的轮询都需要期待,直到WriterThread释放掉写入独占权后,才能获得到数据的访问权,应该留意的是,上述打印信息很明显显示出,可以多个线程同时获得数据的调用权,这从ReadThread1和ReadThread2的信息交互输出可以看出。
七、SynchronizationAttribute
本文来自电脑杂谈,转载请注明本文网址:
http://www.pc-fly.com/a/jisuanjixue/article-124836-1.html
他不会因为南海而丢了中东和欧洲
酒水暴力又打
请问你夫人今晚有空吗