Eogene

  博客园 :: 首页 :: 博问 :: 闪存 :: 新随笔 :: 联系 :: 订阅 订阅 :: 管理 ::

using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading;
using System.Runtime.CompilerServices;

namespace Test
{
/// <summary>
/// http://www.cnblogs.com/michaelxu/archive/2008/09/20/1293716.html
/// </summary>
public class ThreadTest
{
/// <summary>
/// volatile是最简单的一种同步方法,当然简单是要付出代价的。它只能在变量一级做同步,volatile的含义就是告诉处理器,
/// 不要将我放入工作内存, 请直接在主存操作我。因此,当多线程同时访问该变量时,都将直接操作主存,从本质上做到了变量共享。
/// </summary>
public class A
{
private volatile int i;
public int I
{
get { return i; }
set { i = value; }
}
}


/// <summary>

/// Synchronization属性
/// 当我们确定某个类的实例在同一时刻只能被一个线程访问时,我们可以直接将类标识成Synchronization的,这样,CLR会自动对这个类实施同步机制,实际上,
/// 这里面涉及到同步域的概念,当类按如下设计时,我们可以确保类的实例无法被多个线程同时访问
/// </summary>
[System.Runtime.Remoting.Contexts.Synchronization]
public class B
{

}

object lockThis = new object();
/// <summary>
/// lock是一种比较好用的简单的线程同步方式,它是通过为给定对象获取互斥锁来实现同步的。
/// 它可以保证当一个线程在关键代码段的时候,另一个线程不会进来,它只能等待,等到那个线程对象被释放,也就是说线程出了临界区
/// 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一个字符串
/// </summary>
public void LockFun()
{

lock (lockThis)
{
}
}

/// <summary>
/// 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
/// </summary>
public void MonitorFun()
{
if (!Monitor.TryEnter(lockThis))
{
return;
}
try
{
System.Threading.Monitor.Enter(lockThis);
Console.WriteLine("Test");
}finally{
System.Threading.Monitor.Exit(lockThis);
}

}

/// <summary>
/// 在使用上,Mutex与上述的Monitor比较接近,不过Mutex不具备Wait,Pulse,PulseAll的功能,因此,我们不能使用Mutex实现类似的唤醒的功能。
/// 不过Mutex有一个比较大的特点,Mutex是跨进程的,因此我们可以在同一台机器甚至远程的机器上的多个进程上使用同一个互斥体。
/// 尽管Mutex也可以实现进程内的线程同步,而且功能也更强大,但这种情况下,还是推荐使用Monitor,因为Mutex类是win32封装的,
/// 所以它所需要的互操作转换更耗资源
/// </summary>
public void MutexFun()
{
bool created;
System.Threading.Mutex mutex = new System.Threading.Mutex(false, "Test",out created);
if(created)
{

}else
{

}
}

/// <summary>
/// MethodImplAttribute
   /// 如果临界区是跨越整个方法的,也就是说,整个方法内部的代码都需要上锁的话,使用MethodImplAttribute属性会更简单一些。
/// 这样就不用在方法内部加锁了,只需要在方法上面加上 [MethodImpl(MethodImplOptions.Synchronized)] 就可以了,
/// MehthodImpl和MethodImplOptions都在命名空间System.Runtime.CompilerServices 里面。但要注意这个属性会使整个方法加锁,直到方法返回,才释放锁。
/// 因此,使用上不太灵活。如果要提前释放锁,则应该使用Monitor或lock
/// </summary>
[MethodImpl(MethodImplOptions.Synchronized)]
public void MethodImplFun()
{

}

/// <summary>
/// ReaderWriterLock
   /// 在考虑资源访问的时候,惯性上我们会对资源实施lock机制,但是在某些情况下,我们仅仅需要读取资源的数据,
/// 而不是修改资源的数据,在这种情况下获取资源的独占权无疑会影响运行效率,因此.Net提供了一种机制,
/// 使用ReaderWriterLock进行资源访问时,如果在某一时刻资源并没有获取写的独占权,那么可以获得多个读的访问权,
/// 单个写入的独占权,如果某一时刻已经获取了写入的独占权,那么其它读取的访问权必须进行等待
/// </summary>
private static ReaderWriterLock m_readerWriterLock = new ReaderWriterLock();
private static int m_int = 0;
private static void Read()
{
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();
}
}

private static void Writer()
{
while (true)
{
Console.WriteLine("ThreadName " + Thread.CurrentThread.Name + " AcquireWriterLock");
m_readerWriterLock.AcquireWriterLock(1000);
Interlocked.Increment(ref m_int);
Thread.Sleep(5000);
m_readerWriterLock.ReleaseWriterLock();
Console.WriteLine("ThreadName " + Thread.CurrentThread.Name + " ReleaseWriterLock");
}
}


static AutoResetEvent autoResetEvent = new AutoResetEvent(false);
/// <summary>
/// AutoResetEvent
/// 用lock和Monitor可以很好地起到线程同步的作用,但它们无法实现线程之间传递事件。如果要实现线程同步的同时,线程之间还要有交互,就要用到同步事件。同步事件是有两个状态(终止和非终止)的对象,它可以用来激活和挂起线程。
   /// 同步事件有两种:AutoResetEvent和 ManualResetEvent。它们之间唯一不同的地方就是在激活线程之后,状态是否自动由终止变为非终止。
/// AutoResetEvent自动变为非终止,就是说一个AutoResetEvent只能激活一个线程。而ManualResetEvent要等到它的Reset方法被调用,
/// 状态才变为非终止,在这之前,ManualResetEvent可以激活任意多个线程。
/// </summary>
public static void AutoRestEventFun()
{
Thread thread = new Thread(new ThreadStart(ReadThreadProc));
readerThread.Name = "ReaderThread";
readerThread.Start();
for (int i = 1; i <= 10; i++)
{
Console.WriteLine("MainThread writing : {0}", i);
//主(写)线程将数据写入
//主(写)线程发信号,说明值已写过了
//即通知正在等待的线程有事件发生
autoResetEvent.Set();
Thread.Sleep(0);
}
//终止线程
readerThread.Abort();
}

static void ReadThreadProc()
{
while (true)
{
//在数据被写入前,读线程等待(实际上是等待写线程发出数据写完的信号)
autoResetEvent.WaitOne();
Console.WriteLine("");
}
}
}
}

posted on 2012-03-09 10:53  EoGene  阅读(407)  评论(0编辑  收藏  举报