线程池-使用等待事件处理器及超时

本节将描述如何在线程池中对操作实现超时,以及如何在线程池中正确的等待。

代码Demo:

using System;
using System.Threading;

在Main方法下面加入以下代码片段:

static void RunOperations(TimeSpan workerOperationTimeout)
{
using (var evt = new ManualResetEvent(false))
using (var cts = new CancellationTokenSource())
{
Console.WriteLine("Registering timeout operations...");
var worker = ThreadPool.RegisterWaitForSingleObject(
evt, (state, isTimedOut) => WorkerOperationWait(cts, isTimedOut), null, workerOperationTimeout, true);
Console.WriteLine("Starting long running operation...");
ThreadPool.QueueUserWorkItem(_ => WorkerOperation(cts.Token, evt));
Thread.Sleep(workerOperationTimeout.Add(TimeSpan.FromSeconds(2)));
worker.Unregister(evt);
}
}
static void WorkerOperation(CancellationToken token, ManualResetEvent evt)
{
for (int i = 0; i < 6; i++)
{
if (token.IsCancellationRequested)
{
return;
}
Thread.Sleep(TimeSpan.FromSeconds(1));
}
evt.Set();
}
static void WorkerOperationWait(CancellationTokenSource cts, bool isTimedOut)
{
if (isTimedOut)
{
cts.Cancel();
Console.WriteLine("Worker operation timed out and was canceled.");
}
else
{
Console.WriteLine("Worker operation succeded.");
}
}

在Main方法中加入以下代码片段:

RunOperations(TimeSpan.FromSeconds(3));
RunOperations(TimeSpan.FromSeconds(7));

工作原理:

线程池还有一个有用的方法:ThreadPool.RegisterWaitForSingleObject。该方法允许我们将回调函数放入线程池中的队列中。当提供的等待事件处理器收到信号或发生超时时,该回调函数将被调用。这允许我们为线程池中的操作实现超时功能。

首先按顺序向线程池中放入一个耗时长的操作。他运行6秒钟然后一旦成功完成,会设置一个ManualResetEvent信号类。其它的情况下,比如需要取消操作,则该操作会被丢弃。

然后我们注册了第二个异步操作。当从ManualResetEvent对象接受一个信号后,该异步操作会被调用。如果第一个操作顺利完成,会设置该信号量。另一种情况是第一个操作还未完成就已经超时。如果发生了该情况,我们会使用CancellationToken来取消第一个操作。

最后,为操作提供5秒钟的超时时间是不够的。这是因为操作会浪费6秒来完成,只能取消该操作。所以如果提供7秒的超时时间是可行的,该操作会顺利完成。

注意:当有大量的线程必须处于阻塞状态中等待一些多线程事件发信号时,以上方式非常有用。借助于线程池的基础设施,我们无需阻塞所有这样的线程。可以释放这些线程直到信号事件被设置。在服务器端应用程序中这是个非常重要的应用场景,因为服务器端应用程序要求高伸缩性及高性能。

posted @ 2018-07-06 15:59  v-haoz  阅读(289)  评论(0编辑  收藏  举报