超时结束后触发事件

时间:2011-01-27 12:31:12

标签: c# multithreading events wait waitone

我必须等待触发事件。我的初始解决方案是使用AutoResetEventWaitOne(),但在等待超时结束后,事件始终触发。所以我回到下面的方法,但我仍然有同样的问题。超时结束后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事件,因为我没有及时找到其他解决方案。

2 个答案:

答案 0 :(得分: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

答案 1 :(得分:2)

嗯,您需要做的第一件事是确保DoStuff实际上在后台线程中有效。

如果这是正确的,你的代码现在的编写方式,你不需要生成第二个线程,只需要在下面的一行加入它,这样的东西就可以了(作为测试):< / p>

// 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。当然,如果您需要帮助,则需要提供更多详细信息。