正确的等待事件的方式

时间:2015-09-04 07:48:04

标签: c# multithreading events task

我们正在等待函数发生事件。但我不认为守则是正确的(它有效,但对我来说看起来不对!)。

首先,这是我的同事写的代码:

    public string Dispatch(Request data)
    {
        var uri = ...
        string _result = null;
        using (var ws = new WebSocket(uri))
        {
            ws.OnMessage += (sender, e) =>
            {
                _result = e.Data;
            };

            ws.Send(request);
            while (_result == null)
            {
                Thread.Sleep(10);
            }

            return _result;
        }
    }

有没有更好的方法来实现这一目标?我想我可以使用AutoResetEvent,但这样更好吗?有没有办法实现代码,线程可以在等待答案时重用? (我知道如何使用TaskCompletitionSource,但这对于同步功能也是正确的吗?)

我的想法是:

    public string Dispatch(Request data)
    {
        var uri = ...

        using (var ws = new WebSocket(uri))
        {
            TaskCompletionSource<Guid> tcs;
            ws.OnMessage += (sender, e) =>
            {
                tcs.SetResult(e.Data);
            };

            ws.Send(request);

            return tcs.Task.Result;
        }
    }

    public string Dispatch(Request data)
    {
        var uri = ...
        string _result = null;
        var event = new AutoResetEvent(false);
        using (var ws = new WebSocket(uri))
        {
            TaskCompletionSource<Guid> tcs;
            ws.OnMessage += (sender, e) =>
            {
                _result = e.Data;
                event.Set();
            };

            ws.Send(request);

            event.WaitOne();
            return _result;
        }
    }

1 个答案:

答案 0 :(得分:0)

  

有没有更好的方法来实现这个目标?

几乎任何形式的等待都比原始代码更好,原始代码每隔10毫秒唤醒一次,只是为了检查一个标志。唯一更糟的是根本不打电话给Thread.Sleep()。但那只会变得更糟。

  

我想我可以使用AutoResetEvent,但这样更好吗?

恕我直言,最好使用TaskCompletionSourceawait。这样做可以解决您的下一个问题:

  

有没有办法实现代码,线程可以在等待答案时重用?

即。使用await,该线程实际上已被释放用于其他用途。调用await的方法将返回await语句,允许该线程继续执行其他代码。当然,这意味着调用方法需要以某种方式处理异步;通常它的工作方式是async一直被推回到线程的顶部,例如在某种事件调度循环中,例如在Winforms或WPF中找到的。

您并不具体说明为什么您的公司未使用await。如果限制是由于被锁定到早期版本的.NET,您可能也无法使用TaskCompletionSource,因为它仅在.NET 4及更高版本中可用。

即使您使用的是.NET 4(或更高版本)并且可以使用TaskCompletionSource,等待该任务也无法解决您将该线程释放用于其他用途的问题。这可能是也可能不是可寻址的; Task类确实有ContinueWith()方法,它允许您显式提供在完成Task时调用的连续委托。但是如果你的Dispatch()方法本身不能在异步上下文中使用 - 即它在操作完成之前无法返回 - 那么你别无选择,只能在操作完成之前阻止该线程的执行。

(根据上下文 - 这里提供的内容太少,无法提供具体建议 - 您可以从 Dispatch()方法调度其他操作但这会使设计复杂化。恕我直言,最好只升级到.NET 4.5 / C#5并使用await ......这可能不是一个长期的维护问题,而不是试图入侵由更现代的框架和语言版本提供的功能。

最后,我会注意到,作为一般规则,您应该避免使用ManualResetEventAutoResetEvent,因为它们基于非托管Windows同步对象并且相当“重量级”。 .NET提供了更高效的本机同步机制。至少,您可以使用Monitor类,Wait()Pulse()方法;可以使用CountdownEventSemaphoreSlim找到等效机制。每个都有略微不同的语义,但都可以用来轻松地在线程之间实现一个简单的信号。