在多线程中编程中,少不了需要对共享资源的同步。此时就需要对资源同步的相关知识,在C#中,提供了如下几种线程资源同步的方式。
当一个字段被声明为volatile关键字时,CLR中一些管理代码和内存的内部机制将负责对字段进行同步,并且总能保证读取到的字段信息都为最新的值,。
声明为volatile的关键字必须具备如下:
class TestVolatile
{
public volatile int Number;
}
在多线程中对整数自增操作,我们都容易忽略其操作的原子性。如
int i = 0;
i += 1;
i += i 可以分为三步
在多线程中上述任何一步操作都有可能被打断,从而导致实际的值并不是我们所需要的,此时就需要保证操作的原子性。
.NET框架提供Interlock类来提供原子性,其中有三个方法。
Increment //自增
Decrement //自减
Exchange //交换
int i = 0;
System.Threading.Interlocked.Increment(ref i);
Console.WriteLine(i); System.Threading.Interlocked.Decrement(ref i);
Console.WriteLine(i);
System.Threading.Interlocked.Exchange(ref i, 100);
Console.WriteLine(i);
lock关键字获得一个对象的侵占权,提供了简单资源的同步方式,.NET中不提倡lock(this)
简单使用
Object o = new Object
lock(0)
{
.....
}
该类提供了与lock关键字类似的功能,而与lock不同的是,能够更好的控制同步块,该类Enter(object o)方法调用后,会获取o的侵占权。直到调用Exit(object o)方法时才会释放。且Enter与Exit是配套的,并且还该类提供了TryEnter方法来尝试侵占
class Program
{
private static object m_monitorObject = new object();
static void Main(string[] args)
{
Thread thread = new Thread(Do);
thread.Name = "Thread1";
Thread thread2 = new Thread(Do);
thread2.Name = "Thread2";
thread.Start();
thread2.Start();
thread.Join();
thread2.Join();
}
static void Do()
{
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);
}
}
当你走到餐厅时候,发现没有位子,于是你开始等待。当餐厅有空位子时,通知服务生给你安排了一个位置,于是你开始就餐
Monitor提供了三个静态方法:
Monitor.Pulse(Object o)
Monitor.PulseAll(Object o)
Monitor.Wait(Object o ) // 重载函数
可以解决上述问题。
当调用Wait方法时,线程释放资源的独占锁,并且阻塞在Wait方法,直到另外的线程获取资源的独占锁后,更新资源信息并调用Pulse方法后返回。
private static object minotorObject = new object();
static void Main(string[] args)
{
Thread t1 = new Thread(new ThreadStart(HaveLunch));
Thread t2 = new Thread(new ThreadStart(AdvanceFreeChange));
t1.Start();
Console.WriteLine("Wait for free seats");
Thread.Sleep(2000);
t2.Start();
Console.Read();
}
public static void HaveLunch()
{
lock(minotorObject)
{
Console.WriteLine("Customer Wait free seats");
Monitor.Wait(minotorObject);
Console.WriteLine("Oh I am eating...");
}
}
public static void AdvanceFreeChange()
{
lock(minotorObject)
{
Console.WriteLine("This have free seats");
Monitor.Pulse(minotorObject);
}
}
执行结果,当使用Wait方法后,HaveLunch方法阻塞在调用地方,直到Pluse调用后,返回。
Mutex虽然大多数与Minotor相同,但是其不具备Wait Pluse PluseAll方法,即不具备通知响应的功能,并且Mutex是跨进程的
private static Mutex mutex = new Mutex();
static void Main(string[] args)
{
for(int i = 0; i < 3; i++)
{
Thread thread = new Thread(new ThreadStart(ThreadPro));
thread.Name = String.Format("Thread{0}", i + 1);
thread.Start();
}
}
public static void ThreadPro()
{
for(int i = 0; i < 5; i++)
{
UserResource();
}
}
private static void UserResource()
{
try
{
mutex.WaitOne();
Console.WriteLine("{0} has entered the protected area", Thread.CurrentThread.Name);
Thread.Sleep(500);
Console.WriteLine("{0} is leaving the protected area\r\n",Thread.CurrentThread.Name);
}
finally
{
mutex.ReleaseMutex();
}
}