将ManualResetEvent包装为等待任务

我想等待手动重置事件,超时并观察取消。 我想出了类似下面的东西。 手动重置事件对象由我无法控制的API提供。 有没有办法在不接受和阻止ThreadPool的线程的情况下实现这一点?

static Task TaskFromWaitHandle(WaitHandle mre, int timeout, CancellationToken ct) { return Task.Run(() => { bool s = WaitHandle.WaitAny(new WaitHandle[] { mre, ct.WaitHandle }, timeout) == 0; ct.ThrowIfCancellationRequested(); return s; }, ct); } // ... if (await TaskFromWaitHandle(manualResetEvent, 1000, cts.Token)) { // true if event was set } else { // false if timed out, exception if cancelled } 

[已编辑]显然,使用RegisterWaitForSingleObject 是有意义的 。 我试一试。

RegisterWaitForSingleObject将等待组合到专用的服务器线程上,每个线程都可以在多个句柄上等待(具体来说,其中63个, MAXIMUM_WAIT_OBJECTS减去一个“控制”句柄)。

所以你应该可以使用这样的东西(警告:未经测试):

 public static class WaitHandleExtensions { public static Task AsTask(this WaitHandle handle) { return AsTask(handle, Timeout.InfiniteTimeSpan); } public static Task AsTask(this WaitHandle handle, TimeSpan timeout) { var tcs = new TaskCompletionSource(); var registration = ThreadPool.RegisterWaitForSingleObject(handle, (state, timedOut) => { var localTcs = (TaskCompletionSource)state; if (timedOut) localTcs.TrySetCanceled(); else localTcs.TrySetResult(null); }, tcs, timeout, executeOnlyOnce: true); tcs.Task.ContinueWith((_, state) => ((RegisteredWaitHandle)state).Unregister(null), registration, TaskScheduler.Default); return tcs.Task; } } 

您还可以使用与ManualResetEvent类似的SemaphoreSlim.WaitAsync()