除了Lock()、Monitor之外,我們最長(zhǎng)用的就是Mutex了,但是玩不好Mutex就總會(huì)造成死鎖或者AbandonedMutexException(我就玩的不怎么好,在并發(fā)性訪問測(cè)試的時(shí)候總是遇到關(guān)于Mutex的問題,各位線蟲見笑了,不過還是把我遇到的一些問題和總結(jié)拿出來(lái)和大家分享,有誤的地方還往指正。
還是先舉一個(gè)簡(jiǎn)單的例子,來(lái)說明一下這個(gè)東西:
public class ThreadMutex
{
public void Test()
{
Thread t1 = new Thread(Thread1);
Thread t2 = new Thread(Thread2);
t1.Start();
t2.Start();
}
public void Thread1()
{
Mutex m = new Mutex(false, "test");
bool b2 = m.WaitOne();
Console.WriteLine("Thread1 get the mutex : " + b2);
Thread.Sleep(10000);
m.ReleaseMutex();
}
public void Thread2()
{
Mutex m = new Mutex(false, "test");
bool b2 = m.WaitOne();
Console.WriteLine("Thread2 get the mutex : " + b2);
Thread.Sleep(1000);
m.ReleaseMutex();
}
}
恩,Thread1中Mutex.WaitOne()后,就想到與Thread1拿到了Mutex所有權(quán),這時(shí)Thread2得到了同樣的Mutex,然后Mutex.WaitOne(),也想拿到Mutex的所有權(quán),這時(shí)就必須等待了。這里只需要兩點(diǎn)就能明白什么是Mutex了:
1. Mutex是一個(gè)令牌,當(dāng)一個(gè)線程拿到這個(gè)令牌時(shí)運(yùn)行,另外想拿到令牌的線程就必須等待,直到拿到令牌的線程釋放令牌。沒有所有權(quán)的線程是無(wú)法釋放令牌的。
2. Mutex(false,”string”)中的string是令牌的關(guān)鍵,或者可以叫令牌名,因?yàn)镸utex是跨進(jìn)程的,整個(gè)系統(tǒng)中只會(huì)有唯一的令牌存在所以,也就是說你在一個(gè)應(yīng)用程序中的一個(gè)線程中得到了Mutex的所有權(quán),那在另外一個(gè)線程中的另外的線程想得到他就必須要等待。
要弄清楚Mutex就還需要弄清楚兩個(gè)很重要的問題:
1.那就是Mutex是調(diào)用的Win32 的API
HANDLE CreateMutex(
LPSECURITY_ATTRIBUTES lpMutexAttributes,
BOOL bInitialOwner,
LPCTSTR lpName
);
這就是他為什么能跨進(jìn)程訪問的原因,正是由于它使用P/Invoke,他的效率問題就凸現(xiàn)出來(lái),明顯不如Monitor之類的快,用的時(shí)候還需多多斟酌。
下面放一個(gè)Mutex的簡(jiǎn)單實(shí)現(xiàn),看看Mutex在.net下是如何實(shí)現(xiàn)的。
2.Mutex的生命周期,這個(gè)問題讓我郁悶了很久,因?yàn)椴惶私釳utex的機(jī)制,使得我也沒法弄清楚到底能活多長(zhǎng)時(shí)間,這也是AbandonedMutexException經(jīng)常會(huì)出現(xiàn)的原因。還是先來(lái)看一段程序:
public class ThreadMutex
{
public void Test()
{
Thread t1 = new Thread(Thread1);
Thread t2 = new Thread(Thread2);
t1.Start();
t2.Start();
}
public void Thread1()
{
Mutex m = new Mutex(false, "test");
bool b2 = m.WaitOne();
Console.WriteLine("Thread1 get the mutex : " + b2);
}
public void Thread2()
{
Thread.Sleep(10);//保證Thread1執(zhí)行完
Mutex m = new Mutex(false, "test");
bool b2=m.WaitOne();
Console.WriteLine(b2);
m.ReleaseMutex();
}
}
在Thread2中的WaitOne()方法就會(huì)報(bào)錯(cuò)了,AbandonedMutexException,原因就是Thread1拿到了Mutex后沒有釋放,Thread1就結(jié)束了,這樣Mutex成了被拋棄的地孩子了,呵呵。但是如果垃圾收集了,就不一樣咯。代碼稍微修改了一下:
public class ThreadMutex
{
public void Test()
{
Thread t1 = new Thread(Thread1);
Thread t2 = new Thread(Thread2);
t1.Start();
t2.Start();
}
public void Thread1()
{
Mutex m = new Mutex(false, "test");
bool b2 = m.WaitOne();
Console.WriteLine("Thread1 get the mutex : " + b2);
}
public void Thread2()
{
Thread.Sleep(10);//保證Thread1執(zhí)行完
GC.Collect();
GC.WaitForPendingFinalizers();
bool b1;
Mutex m = new Mutex(false, "test",out b1);
Console.WriteLine(b1);
bool b2=m.WaitOne();
Console.WriteLine(b2);
m.ReleaseMutex();
}
}
結(jié)果是:
Thread1 get the mutex : True
True
True
Thread2里面的Mutex是新創(chuàng)建的,呵呵,這里面的玄妙自己體會(huì)吧。
最后要說一下的是Mutex的訪問和window訪問文件的機(jī)制基本上是一樣的,window訪問對(duì)象和訪問文件使用的是同樣的安全機(jī)制(雖然我還沒看懂)。