0

我必须等待触发事件。我最初的解决方案是使用AutoResetEventand WaitOne(),但事件总是在等待超时结束后触发。所以我回到了下面的方法,但我仍然有同样的问题。超时结束后 2 或 3 秒,无论超时是什么,都会触发事件。

_wait = true;
_delayedResponse = null;

var thread = new Thread(delegate
{
        while (_wait)
        {
           Thread.Sleep(500);
           if (_delayedResponse != null)
               return;
        }
});

thread.Start();
var received = thread.Join(_responseTimeout);
_wait = false;

if (!received)
    throw new TimeoutException(
        "Timeout for waiting for response reached.");

return _delayedResponse;

这是事件处理程序代码:

private void OnResponseArrived(object sender, ResponseEventArgs args)
{
    _delayedResponse = args.VerificationResponse;
}

事件本身由调用上述函数的另一个函数触发。基本上它看起来像这样:

var result = DoStuff(); // Library function that is responsible for the event 
if (result.Status == Status.Wait)
   Wait(); // Function above

有谁知道导致这个问题的原因以及我该如何解决?

编辑:不再相关。转发了 OnResponseArrived 事件,因为我没有及时找到其他解决方案。

4

2 回答 2

2

Thread.Join是一个阻塞调用——它会阻止你调用的线程做任何其他工作。我的猜测是您正在后台线程上等待事件,但是引发事件的代码与您发布的代码在同一线程上运行。

通过调用thread.Join,您正在阻塞应该进行处理的线程。因此,您等待超时到期......然后无论您发布的代码采用哪种方法完成......然后您的处理实际发生并ResponseArrived引发事件。

如果您发布其余代码会很有用,但解决方案的要点是ResponseArrived在后台线程中运行实际工作(无论代码引发事件) - 并从您发布的代码中删除额外的线程.

编辑以回应评论...

为了同步您的两段代码,您可以使用AutoResetEvent. 而不是使用Thread.Sleep和你的其他代码,尝试这样的事情:

// create an un-signalled AutoResetEvent
AutoResetEvent _waitForResponse = new AutoResetEvent(false);

void YourNewWorkerMethod()
{
    _delayedResponse = null;
    var result = DoStuff();

    // this causes the current thread to wait for the AutoResetEvent to be signalled
    // ... the parameter is a timeout value in milliseconds
    if (!_waitForResponse.WaitOne(5000))
        throw new TimeOutException();

    return _delayedResponse;
}


private void OnResponseArrived(object sender, ResponseEventArgs args)
{
    _delayedResponse = args.VerificationResponse;
    _waitForResponse.Set();  // this signals the waiting thread to continue...
}

请注意,您需要AutoResetEvent在完成后处理它。

于 2011-01-27T12:40:48.533 回答
2

好吧,您需要做的第一件事是确保它DoStuff确实在后台线程中工作。

如果这是正确的,那么您现在编写代码的方式,您不需要生成第二个线程,只需在下面加入一行,这样的事情就可以简单地工作(作为测试):

// handler needs to be attached before starting
library.ResponseReceived += OnResponseReceived;

// call the method
var result = library.DoStuff();

// poll and sleep, but 10 times max (5s)
int watchdog = 10;
while (_delayedResponse == null && watchdog-- > 0)
   Thread.Sleep(500);

// detach handler - always clean up after yourself
library.ResponseReceived -= OnResponseReceived;

Console.WriteLine(_delayedResponse != null);

如果这可行,并且您正在编写一个 WinForms 应用程序,那么您应该考虑在后台线程中完成整个事情,然后在完成时通知 UI。当然,如果您需要帮助,您将需要提供更多详细信息。

于 2011-01-27T12:52:18.787 回答