1:CountdownEvent
Framework 4.0提供了一个CountdownEvent类,主要是让你等待多个线程。考虑下这样的场景:
有一个任务,3个人去做,你需要等这3个人都做完了才继续下一步操作。
下面就是:
class
Thread15
{
static
CountdownEvent _countdown
=
new
CountdownEvent(
3
);
public
static
void
MainThread()
{
new
Thread(SaySomething).Start(
"
I'm thread 1
"
);
new
Thread(SaySomething).Start(
"
I'm thread 2
"
);
new
Thread(SaySomething).Start(
"
I'm thread 3
"
);
_countdown.Wait();
//
阻塞,直到Signal被调用三次
Console.WriteLine(
"
All threads have finished speaking!
"
);
}
static
void
SaySomething(
object
thing)
{
Thread.Sleep(
1000
);
Console.WriteLine(thing);
_countdown.Signal();
}
}
注意在构造函数中我们传递了3.代表我们要等待3个线程都结束。
2:ThreadPool.RegisterWaitForSingleObject
如果你的应用程序中有大量的线程都在一个WaitHandle上花费了大量的时间的时候,
你可以通过线程池的ThreadPool.RegisterWaitForSingleObject方法来提高资源的利用率,这个方法接受一个委托,当这个WaitHandle调用signal方法后,
方法的委托就会被执行了。
class
Thread16
{
static
ManualResetEvent _starter
=
new
ManualResetEvent(
false
);
public
static
void
MainThread()
{
RegisteredWaitHandle reg
=
ThreadPool.RegisterWaitForSingleObject
(_starter, Go,
"
Some Data
"
,
-
1
,
true
);
//
在_starter上等待执行Go方法,-1,代表永远不超时,true代表只执行一遍,”Some Data”是传递的参数
Thread.Sleep(
5000
);
Console.WriteLine(
"
Signaling worker...
"
);
_starter.Set();
//
唤醒等待的线程
Console.ReadLine();
reg.Unregister(_starter);
//
取消注册。
}
static
void
Go(
object
data,
bool
timeOut)
{
Console.Write(
"
Started -
"
+
data);
Console.WriteLine(
"
ThreadId:
"
+
Thread.CurrentThread.ManagedThreadId);
}
}
3:同步上下文:
通过继承ContextBoundObject类,并且加上Synchronization特性,CLR会自动的为你的操作加锁。
//
继承自ContextBoundObject,且加上Synchronization特性修饰
[Synchronization]
public
class
AutoLock:ContextBoundObject
{
public
void
Demo()
{
Console.WriteLine(
"
Start...
"
);
Thread.Sleep(
1000
);
Console.WriteLine(
"
end
"
);
}
}
//
主线程:
public
static
void
MainThread()
{
AutoLock safeInstance
=
new
AutoLock();
new
Thread(safeInstance.Demo).Start();
new
Thread(safeInstance.Demo).Start();
safeInstance.Demo();
}
输出为:
Start…
End
Start…
End
Start…
End
CLR会确保一次只有一个线程可以执行safeInstance里面的代码,它会自动的创建一个同步对象,然后
在每次调用方法或属性的时候都 lock它,从这个角度来讲safeInstance是一个同步上下文。
但是它是怎么样工作的,在Synchronization特性和System.Runtime.Remoting.Contexts命名空间中存在着线索。
一个ContextBoundObject被认为是一个远程(“remote”)对象.意味所有的方法调用都可以被介入。当我们实例化AutoLock的时候,CLR实际上返回了一个proxy对象,一个和AutoLock对象有着同样方法,同样属性的Proxy对象,在这里它扮演者中介的对象。这样就为自动加锁提供了介入的机会,在每一次方法调用上都会花费几微妙的时间来介入。
你可能会认为下面的代码会和上面的一样,是一样的输出结果:
//
继承自ContextBoundObject,且加上Synchronization特性修饰
[Synchronization]
public
class
AutoLock:ContextBoundObject
{
public
void
Demo()
{
Console.WriteLine(
"
Start...
"
);
Thread.Sleep(
1000
);
Console.WriteLine(
"
end
"
);
}
public
void
Test()
{
new
Thread(Demo).Start();
new
Thread(Demo).Start();
new
Thread(Demo).Start();
Console.ReadLine();
}
public
static
void
RunTest()
{
new
AutoLock().Test();
}
}
public
static
void
MainThread()
{
//
AutoLock safeInstance = new AutoLock();
//
new Thread(safeInstance.Demo).Start();
//
new Thread(safeInstance.Demo).Start();
//
safeInstance.Demo();
AutoLock.RunTest();
}
实际上这里我们会运行到Console.ReadLine方法这里,然后等待输入。
为什么??
因为CLR会确保一次只有一个线程能够执行AutoLock的代码,所以当主线程执行到Console.ReadLine方法的时候,
就开始等待输入了。如果你按下Enter,代码就和上面的输出一样了。
注:还有一些同步构造将在以后讲到.
参考资料:
http://www.albahari.com/threading/
CLR Via C# 3.0