首页
学习
活动
专区
圈层
工具
发布
社区首页 >问答首页 >如何制作非阻塞等待句柄?

如何制作非阻塞等待句柄?
EN

Stack Overflow用户
提问于 2015-12-31 15:59:31
回答 3查看 1.8K关注 0票数 4

本质上,我所做的是创建一个web服务器来处理一个API调用,然后当它完成后继续方法的执行,所以本质上:

代码语言:javascript
复制
new WebServer(myAutoResetEvent);
myAutoResetEvent.WaitOne();

但是,在此之前,这会阻塞线程。有什么办法使这个异步吗?把它封装在一个await Task.Run()调用(即await Task.Run(() => myAutoResetEvent.WaitOne()) )中可以吗?

谢谢!

EN

回答 3

Stack Overflow用户

回答已采纳

发布于 2015-12-31 16:20:47

通常,WebServer控件不应该做任何有趣的事情。应该有一个运行服务器的Task WebServer.RunAsync函数。然后,您可以使用结果任务来同步和协调。

如果您不想这样做,您可以使用TaskCompletionSource<object>作为一个一次性异步事件。

我相信ThreadPool类有一种有效等待设置WaitHandle的方法,但这是一个更糟的解决方案。

票数 5
EN

Stack Overflow用户

发布于 2015-12-31 16:27:42

不应该阻塞ThreadPool线程,这是导致ThreadPool饥饿的一种快速方法,而是提供了一个异步等待WaitHandle实例的方法,这称为ThreadPool.RegisterWaitForSingleObject

通过使用ThreadPool.RegisterWaitForSingleObject,将注册一个回调,以便在WaitHandle可用时调用,不幸的是,这不是异步/等待兼容的,使异步/等待兼容的完整实现如下:

代码语言:javascript
复制
public static class WaitHandleExtensions
{
    public static Task WaitOneAsync(this WaitHandle waitHandle, CancellationToken cancellationToken)
    {    
        return WaitOneAsync(waitHandle, Timeout.Infinite, cancellationToken);
    }

    public static async Task<bool> WaitOneAsync(this WaitHandle waitHandle, int timeout, CancellationToken cancellationToken)
    {    
        // A Mutex can't use RegisterWaitForSingleObject as a Mutex requires the wait and release to be on the same thread
        // but RegisterWaitForSingleObject acquires the Mutex on a ThreadPool thread.
        if (waitHandle is Mutex)
            throw new ArgumentException(StringResources.MutexMayNotBeUsedWithWaitOneAsyncAsThreadIdentityIsEnforced, nameof(waitHandle));

        cancellationToken.ThrowIfCancellationRequested();

        var tcs = new TaskCompletionSource<bool>();
        var rwh = ThreadPool.RegisterWaitForSingleObject(waitHandle, OnWaitOrTimerCallback, tcs, timeout, true);

        var cancellationCallback = BuildCancellationCallback(rwh, tcs);

        using (cancellationToken.Register(cancellationCallback))
        {
            try
            {
                return await tcs.Task.ConfigureAwait(false);
            }
            finally
            {
                rwh.Unregister(null);
            }
        }
    }

    private static Action BuildCancellationCallback(RegisteredWaitHandle rwh, TaskCompletionSource<bool> tcs)
    {    
        return () =>
        {
            if (rwh.Unregister(null))
            {
                tcs.SetCanceled();
            }
        };
    }

    private static void OnWaitOrTimerCallback(object state, bool timedOut)
    {    
        var taskCompletionSource = (TaskCompletionSource<bool>)state;

        taskCompletionSource.SetResult(!timedOut);
    }
}

唯一的限制是这不能与Mutex一起使用。

这可以这样使用:

代码语言:javascript
复制
await myAutoResetEvent.WaitOneAsync(cancellationToken).ConfigureAwait(false);
票数 3
EN

Stack Overflow用户

发布于 2015-12-31 16:55:04

另一种需要考虑的方法是使用HttpSelfHostServer (System.Web.Http.SelfHost.dll),并将所有线程处理细节留给它的实现。

代码语言:javascript
复制
var config = new HttpSelfHostConfiguration("http://localhost:9999");
var tcs = new TaskCompletionSource<Uri>();

using (var server = new HttpSelfHostServer(config, new MessageHandler(tcs)))
{
    await server.OpenAsync();

    await tcs.Task;

    await server.CloseAsync();
}

return tcs.Task.Result;

class MessageHandler : HttpMessageHandler
{
    private readonly TaskCompletionSource<Uri> _task;

    public MessageHandler(TaskCompletionSource<Uri> task)
    {
        _task = task;
    }

    protected override Task<HttpResponseMessage> SendAsync(HttpRequestMessage request, CancellationToken cancellationToken)
    {
        _task.SetResult(request.RequestUri);
        return Task.FromResult(new HttpResponseMessage(HttpStatusCode.OK));
    }
}
票数 0
EN
页面原文内容由Stack Overflow提供。腾讯云小微IT领域专用引擎提供翻译支持
原文链接:

https://stackoverflow.com/questions/34548740

复制
相关文章

相似问题

领券
问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档