概念性的东西:
互斥锁是一个互斥的同步对象,一个时间只有一个线程可以获取它。
前一篇文章中的时钟程序我们这里用Mutex互斥锁来实现。
class Program
{
static void Main(
string[] args)
{
Clock C =
new Clock();
C.RunClock(1);
Console.Read();
}
}
public class Clock
{
public Mutex Mtx =
new Mutex();
//开始运行时钟,输入运行分钟
public void RunClock(
Int32 Minute)
{
Thread T1 =
new Thread((
object Minute1) =>
{
Int32 m =
Convert.ToInt32(Minute1) * 60 / 2;
while (m > 0)
{
DI(
true);
m--;
}
});
Thread T2 =
new Thread((
object Minute1) =>
{
Int32 m =
Convert.ToInt32(Minute1) * 60 / 2;
while (m > 0)
{
DA(
true);
m--;
}
});
T1.Start(
true);
T2.Start(
true);
}
public void DI(
bool run)
{
Mtx.WaitOne();
if (!run)
{
Mtx.ReleaseMutex();
return;
}
else
{
Console.WriteLine(
"嘀");
Thread.Sleep(1000);
Mtx.ReleaseMutex();
}
}
public void DA(
bool run)
{
Mtx.WaitOne();
if (!run)
{
Mtx.ReleaseMutex();
return;
}
else
{
Console.WriteLine(
"嗒");
Thread.Sleep(1000);
Mtx.ReleaseMutex();
}
}
}
为什么会这样?貌似一切都是合理的!
这里我只发表下我的理解,还请个位园友自己认真分析下,毕竟我个人理解可能是错误的!
如果您发现我理解错了,请在评论指出,我好及时学习更早!
Monitor的时钟是使用了lock来锁定,lock最后也会被编译成为Monitor的Enter和Exit。
重点是Monitor在Pulse(obj)的时候已经就确定了另外一个等待obj被释放的线程拥有了执行权!
而Mutex在ReleaseMutex在释放锁定之后,当前线程和其他等待线程都执行WaitOne,导致了线程执行权的争夺!
随着程序的运行,线程之间的争夺激烈。
mutex互斥锁-杯具的拆弹手
下面用互斥锁实现个例子,一个炸弹,同时只能一个人拆弹,如果超过1个人就爆炸。
class Program
{
static void Main(
string[] args)
{
炸弹 b =
new 炸弹();
b.拆弹();
b.拆弹();
Console.Read();
}
}
public class 炸弹
{
private Int32 热度 = 0;
public Mutex m =
new Mutex();
public void 拆弹()
{
Thread t =
new Thread(炸弹内部);
t.Start();
}
private void 炸弹内部()
{
m.WaitOne();
热度++;
Thread.Sleep(1000);
if (热度>1)
{
Console.WriteLine(
"炸弹爆炸!拆弹手见马克思...");
m.ReleaseMutex();
return;
}
热度--;
Console.WriteLine(
"炸弹安全拆除!拆弹手这个月奖金加倍...");
m.ReleaseMutex();
}
}
拆弹手很幸运,这个月奖金加倍了。如果把Mutex去掉,那么马克思就等着他们去聊天。
信号量-Semaphore
互斥锁和同步都是锁定一个资源,同时只让一个线程去操作。
对于可以允许限定数量线程执行的情况互斥锁就不适合了,这里就需要信号量。
信号量通过一个计数器来控制对共享资源的访问,如果计数器的闲置数大于0,那么就允许访问,如果=0就拒绝访问。
public Semaphore(
int initialCount,
int maximumCount);
initialCount:可以同时授予的信号量的初始请求数。
maximumCount:可以同时授予的信号量的最大请求数。
public virtual bool WaitOne();
public int Release();
WaitOne():阻止当前线程,直到当前 System.Threading.WaitHandle 收到信号。
Release():退出信号量并返回前一个计数。
下面还是请出我们杯具的拆弹手来演示。
class Program
{
static void Main(
string[] args)
{
//有关部门的砖家叫兽告诉拆弹手炸弹最多4个人同时拆
Int32 拆弹手人数 = 4;
炸弹 b =
new 炸弹(拆弹手人数);
while (拆弹手人数 > 0)
{
b.拆弹();
拆弹手人数--;
}
Console.Read();
}
}
public class 炸弹
{
private Int32 最高温度 = 0;
private Int32 热度 = 0;
private Semaphore S;
public 炸弹(
Int32 limit)
{
最高温度 = 3;
S =
new Semaphore(limit, limit);
}
public void 拆弹()
{
Thread t =
new Thread(炸弹内部);
t.Start();
}
private void 炸弹内部()
{
S.WaitOne();
热度++;
Thread.Sleep(1000);
if (热度 > 最高温度)
{
Console.WriteLine(
"炸弹爆炸!拆弹手见马克思...");
S.Release();
return;
}
热度--;
Console.WriteLine(
"炸弹安全拆除!拆弹手这个月奖金加倍...");
S.Release();
}
}
杯具的拆弹手,这里砖家叫兽错误指出最多4个人,而炸弹最高支持3个人!唉。。。
使用事件实现-幸福的小新
事件是另外一种同步对象,ManualResetEvent,AutoResetEvent2个类实现了事件同步的功能,都派生自EventWaitHandle类。
介绍下ManualResetEvent。
public ManualResetEvent(
bool initialState);
public virtual bool WaitOne();
public bool Reset();
public bool Set();
第一个是构造函数,传入的变量是事件是否发出信号。
第二个是等待一个事件发出信号,期间是阻塞的。
第三个是重置事件为未发出信号状态。
最后一个是设置事件为发出信号状态。
ManualResetEvent和AutoResetEvent的主要实现都是相同,只有一点不同,就是ManualResetEvent需要手动设置事件为未发出信号,而AutoResetEvent是自动设置的。
野原新之助的一天
class Program
{
static void Main(
string[] args)
{
ManualResetEvent mal =
new ManualResetEvent(
false);
美女姐姐 美女 =
new 美女姐姐();
小新 新之助 =
new 小新();
Thread t1 =
new Thread(
new ParameterizedThreadStart(美女美女姐姐上街));
Thread t2 =
new Thread(
new ParameterizedThreadStart(新之助.小新出动));
t1.Start(mal);
t2.Start(mal);
Console.Read();
}
public class 美女姐姐
{
public void 美女姐姐上街(
object Mal)
{
Thread.Sleep(2000);
Console.WriteLine(
"青春美丽的美女姐姐上街了...");
Thread.Sleep(1000);
((
ManualResetEvent)Mal).Set();
}
}
public class 小新
{
public 小新()
{
Thread.Sleep(1000);
Console.WriteLine(
"小新拿着奶茶,猥琐的躲在大街边的绿化带里...");
Thread.Sleep(1000);
}
public void 小新出动(
object Mal)
{
ManualResetEvent Mal1 = (
ManualResetEvent)Mal;
Console.WriteLine(
"小新左右寻找美女姐姐...");
//等待美女姐姐的出现
Mal1.WaitOne();
Console.WriteLine(
"小新跑到美女姐姐面前,脱下小裤裤,亮出大象,开始跳大象舞...");
Console.WriteLine(
"屁股扭扭,“大象,大象,大象...”");
Mal1.Reset();
}
}
}
不解释了。。。
总结下吧!
写了这么多,应该好好思考下这些同步方式,每种虽然大概思路是一样!但是绝对有各自的特点。
有空大家还是看看CLR 3版中的线程核心部分,里面把这些同步的原理讲的很清楚。
总之线程的水太深,慢慢游吧,下一篇把剩下的一些线程同步方式讲下,然后就在最后对NET线程同步做个总结。