c# - 等待事件的正确方法

标签 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;
        }
    }

最佳答案

Is there a better way to realize this?

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

I think I can use AutoResetEvent, but is this better?

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

Is there a way to realize the Code that the Thread can be reused while it is waiting for an answer?

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

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

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

(取决于上下文——此处提供的内容太少,无法提供具体建议——您可以在 Dispatch() 方法等待时分派(dispatch)其他操作。但这会使设计大大复杂化。恕我直言,最好只升级到 .NET 4.5/C# 5 并使用 await ……这可能不是一个长期维护问题,而是试图破解固有的功能由更现代的框架和语言版本提供)。

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

关于c# - 等待事件的正确方法,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/32392790/

相关文章:

c# - 如何使用 Application Insight 的持久性 channel 防止丢失遥测 pageView?

python - 线程实时记录

c++ - FAST DMA 受益于在 C++ 中使用线程的 FPGA

java - JDBC 事务与连接说明

python gui 事件乱序

visual-studio - Visual Studio 预构建脚本在 windows/system32 中找不到 exe 文件

C# 我们如何将 header 参数添加到 HTTPCLIENT 对象

c# - 查找 WCF 服务调用方的 Active Directory 域用户名

c# - 图像字节数组未正确保存到数据库

javascript - 如何在 Angular 5 中执行 "onload"事件?