1、Semaphore 和 CountdownEvent的使用
1)Semaphore
Semaphore
限制可同时访问某一资源或资源池的线程数。.NET中的信号量(Semaphore)是操作系统维持的一个整数。当整数位0
时。其他线程无法进入。当整数大于0
时,线程可以进入。每当一个线程进入,整数-1
,线程退出后整数+1
。整数不能超过信号量的最大请求数。信号量在初始化的时候可以指定这个整数的初始值。System.Threading.Semaphore
类的构造函数的两个参数第一个就是信号量的内部整数初始值,也就是初始请求数,第二个参数就是最大请求数。
2)CountdownEvent
System.Threading.CountdownEvent
是在收到信号特定次数后取消阻止等待线程的同步基元。 CountdownEvent
适用于以下情况:不得不先使用 ManualResetEvent
或 ManualResetEventSlim
并手动递减变量,然后再向事件发出信号。 CountdownEvent
表示在计数变为0时处于有信号状态的同步基元,通过信号机制CountdownEvent
当有新的需要同步的任务产生时,就调用AddCount增加它的计数,当有任务到达同步点是,就调用Signal
函数减小它的计数,当CountdownEvent
的计数为零时,就表示所有需要同步的任务已经完成。CountDownEvent与Barrier相似,所不同的是,CountDownEvent
调用成员函数Wait()
将阻塞,直至成员函数Signal()
被调用达特定的次数,这时CountDownEvent
称作就绪态,对于处于就绪态的CountDownEvent
,调用Wait()
函数将不会再阻塞,只有手动调用Reset()
函数后,调用Wait()函数将再次阻塞。CountDownEvent
可以通过TryAddCount()
和AddCount()
函数来增加函数Signal()
需被调用的次数,但只有当CountDownEvent
处于未就绪态时才会成功。否则根据调用函数的不同,将有可能抛出异常。
2、Semaphore的使用
.NET中的信号量(Semaphore
)是操作系统维持的一个整数。当整数位0
时。其他线程无法进入。当整数大于0
时,线程可以进入。每当一个线程进入,整数-1
,线程退出后整数+1
。整数不能超过信号量的最大请求数。信号量在初始化的时候可以指定这个整数的初始值。System.Threading.Semaphore
类的构造函数的两个参数第一个就是信号量的内部整数初始值,也就是初始请求数,第二个参数就是最大请求数。
using System;
using System.Threading;
namespace ConsoleApplication
{
class Program
{
// 一个模拟有限资源池的信号量。
//
private static Semaphore _pool;
// 填充Thread.Sleep()间隔,使输出更有序。
private static int _padding;
public static void Main()
{
//创建一个信号量,这个信号量最多可以满足三个信号量
//并发请求。初始计数为0,
//整个信号量计数是初始的
//主程序线程拥有。
//
_pool = new Semaphore(0, 3);
// 创建并启动五个线程。
//
for (int i = 1; i <= 5; i++)
{
Thread t = new Thread(new ParameterizedThreadStart(Worker));
// 启动线程,传递编号。
t.Start(i);
}
//等待半秒钟,让所有的线程启动和阻塞信号量。
Thread.Sleep(500);
//主线程开始持有整个信号量计数。
// 调用Release(3)带来返回信号量的最大值允许等待的线程进入信号量,一次最多3个。
Console.WriteLine("主线程调用 Release(3)");
_pool.Release(3);
Console.WriteLine("主线程退出");
Console.ReadKey();
}
private static void Worker(object num)
{
//每个工作线程开始请求信号量
Console.WriteLine("Thread {0} 开始 " +
"等待信号量", num);
_pool.WaitOne();
// 填充Thread.Sleep()间隔,使输出更有序。
int padding = Interlocked.Add(ref _padding, 100);
Console.WriteLine("Thread {0} 进入信号量", num);
Thread.Sleep(1000 + padding);
Console.WriteLine("Thread {0} releases the semaphore.", num);
Console.WriteLine("Thread {0} 之前的信号量计数: {1}",
num, _pool.Release());
}
}
}
3、CountdownEvent的使用
CountDownEvent
调用成员函数Wait()
将阻塞,直至成员函数Signal()
被调用达特定的次数,这时CountDownEvent
称作就绪态,对于处于就绪态的CountDownEvent
,调用Wait()
函数将不会再阻塞,只有手动调用Reset()
函数后,调用Wait()
函数将再次阻塞。CountDownEvent
可以通过TryAddCount()
和AddCount()
函数来增加函数Signal()
需被调用的次数,但只有当CountDownEvent
处于未就绪态时才会成功。否则根据调用函数的不同,将有可能抛出异常。
例如,
using System;
using System.Threading;
using System.Threading.Tasks;
namespace ConsoleApplication
{
class Program
{
static CountdownEvent _count = new CountdownEvent(3);
static void Main(string[] args)
{
Task.Factory.StartNew(() =>
{
Thread.Sleep(500);
Console.WriteLine("thread 1 complete");
_count.Signal();
});
Task.Factory.StartNew(() =>
{
Thread.Sleep(1000);
Console.WriteLine("thread 2 complete");
_count.Signal();
});
Task.Factory.StartNew(() =>
{
Thread.Sleep(2000);
Console.WriteLine("thread 3 complete");
_count.Signal();
});
//_count.AddCount();//调用AddCount增加计数
Console.WriteLine("waiting tasks....");
_count.Wait();
Console.WriteLine("all task completed");
Console.ReadKey();
}
}
}