【问题标题】:How can a C# Method return only after Event has been fired?只有在 Event 被触发后,C# 方法才能返回?
【发布时间】:2014-01-14 20:31:19
【问题描述】:

我有一个 DLL SomeLib.dll,其中包含一个 DeleteHandler 对象:

public class DeleteHandler
{
    private _handlerToServerObj;

    public DeleteHandler()
    {
        _handlerToServerObj = new HandlerToServerObj(/* ... */);
        _handlerToServerObj.OnDelete += new OnDeleteEventHandler(OnDeleteEH);
    }

    public void Delete(string id)
    {
        _handlerToServerObj.Delete(id);
    }

    private void OnDeleteEH(string id)
    {
        //
    }
}

它是如何工作的:

  • _handlerToServerObj.Delete(id) 正在向服务器发送消息,调用立即返回;
  • 服务器成功删除项目后,会触发OnDelete事件。

此库通常由WinForms 应用程序使用。

通常,我希望Delete() 方法在服务器确实删除项目后返回(即,OnDelete 事件被触发,因此,OnDeleteEH被执行)。

我尝试使用ManualResetEvent,但没有成功(即从未调用过OnDeleteEH):

private ManualResetEvent _waitHandle = new ManualResetEvent(false);

public void Delete(string id)
{
    _waitHandler.Reset();
    _handlerToServerObj.Delete(id);
    _waitHandle.WaitOne();
}

private void OnDeleteEH(string id)
{
    //
    _waitHandle.Set();
}

或者:

private ManualResetEvent _waitHandle = new ManualResetEvent(false);

public void Delete(string id)
{
    _waitHandler.Reset();
    Thread thread = new Thread(PrivateDelete);
    thread.Start(id);
    _waitHandle.WaitOne();
}

private void PrivateDelete(string id)
{
    _handlerToServerObj.Delete(id);
}

private void OnDeleteEH(string id)
{
    //
    _waitHandle.Set();
}

这个设计有效吗?如果是,我在实施时犯了哪些错误?如果没有,您能否提出更正建议?

[编辑 1]

更正了代码中的一些错误。

【问题讨论】:

    标签: c# .net events event-handling synchronization


    【解决方案1】:

    你在方法中隐藏了你的等待句柄,这导致你在等待一个永远不会被设置的句柄。

    您需要使用实例字段,而不是在Delete 中声明一个名为_waitHandle 的新变量。

    当然,如果您使用匿名事件处理程序而不是其他方法,您可以简化代码:

    public void Delete(string id)
    {
        ManualResetEvent waitHandle = new ManualResetEvent(false);
        _handlerToServerObj.OnDelete += () => waitHandle.Set();
        _handlerToServerObj.Delete(id);
        waitHandle.WaitOne();
    }
    

    如果您在这里使用这样的闭包,那么您根本不需要句柄作为实例字段。


    说了这么多,如果您在桌面 UI 应用程序的上下文中使用它,您可能不想要有一个阻塞方法;你可能想异步地做这样的工作,而不是同步地。如果在 UI 线程中调用,您将阻塞 UI 线程,并创建一个新的工作线程以便它可以将所有时间都用于阻塞某些异步 IO 操作,这只是浪费。虽然在某些情况下您确实希望等待此事件,但请花时间确保它适合您的具体情况。

    【讨论】:

    • +1 请注意,这解决了问题所在的直接问题。在 UI 线程上使用同步等待(SleepWaitOne...)至少会冻结 UI,或者在其他线程需要将结果发布到 UI 线程的情况下会导致死锁。如果可能,请考虑使用新的async/await
    • @AlexeiLevenkov 是的,正在编辑中添加注释。
    • 感谢您的回复。 _handlerToServerObj.OnDelete += waitHandle.Set() 不起作用,因为编译器抱怨它不能将类型“bool”隐式转换为“OnDelete”。此外,我需要OnDeleteEH 事件处理程序来进行一些验证。我在写问题时犯了一个错误(即,在错误的地方声明了_waitHandle)。因此,我已经编辑了这个问题,现在事情似乎已经到位。当然,问题仍然存在。建议?
    • @Yeseanul 未编译的事件处理程序是我只是忘记添加 lambda 部分;已编辑。如果处理程序还需要做其他事情,那就让它做其他事情;没关系。
    • @Yeseanul 鉴于这种情况,人们可以 100% 确信这不应该同步完成。这将阻塞 UI 线程,直到此操作完成;即使你能做到这一点,你也不想这样做。您应该对此进行异步编程,这意味着您的方法应该接受回调,并且调用者应该提供回调方法以在删除发生后运行。
    猜你喜欢
    • 1970-01-01
    • 2011-07-07
    • 2021-06-12
    • 2015-11-21
    • 1970-01-01
    • 2021-10-19
    • 1970-01-01
    • 1970-01-01
    • 2013-11-16
    相关资源
    最近更新 更多