Better time-out detection for synchronous operations
Asked Answered
Z

2

5

I need a way to perform some action synchronously which should complete in half a second, but might just hang around for minutes. If it times out I don't care about the result. Here's the I'm doing it right now using compiler-generated delegate.BeginInvoke:

static void Main()
{
    bool disposed = false;
    var wait = new ManualResetEvent(false);
    var a = new Action(
        () =>
            {
                Thread.Sleep(1000); // <- some looong action

                if (!disposed)
                    lock (wait)
                        if (!disposed)
                            wait.Set();
            });

    a.BeginInvoke(a.EndInvoke, null);

    bool success = wait.WaitOne(500);
    Console.WriteLine(success ? "success" : "timeout");

    lock (wait)
    {
        wait.Dispose();
        disposed = true;
    }

    Console.ReadLine();
}

Looks ugly. And I'm aware lambda closure's disposed variable is modified (unlike my ReSharper, I like this C# feature). All because I want to dispose ManualResetEvent. Can you suggest better approach in .NET4? Perhaps should I just skip disposing the event and rely on GC?

One note: ManualResetEvent.Set() explodes if you try to do it on disposed instance.

Zalea answered 28/5, 2010 at 15:13 Comment(2)
Calling BeginInvoke is how you start an Asynchronous operation, not a Synchronous one.Downstage
Right, but I want to complete it synchronously within time-out interval.Zalea
Z
3

Ugh, now that I looked a bit more at code samples using compiler-generated delegate.BeginInvoke I see that it returns IAsyncResult which has AsyncWaitHandle exactly for my goal:

var a = new Action(() => Thread.Sleep(1000)); // <- some looong action
IAsyncResult asyncResult = a.BeginInvoke(a.EndInvoke, null);
bool success = asyncResult.AsyncWaitHandle.WaitOne(500);

That wait handle in case of AsyncResult is in fact an instance of ManualResetEvent which is disposed automatically from thread-pool thread right when my asynchronous call completes.

Zalea answered 28/5, 2010 at 16:13 Comment(1)
Yup. But no real difference, only EndInvoke() can dispose it. Which you can't call if the wait timed-out. Don't bother, kernel events are dirt cheap and peanuts compared to that thread you kept running.Margaux
P
4

No, you should not skip the calling of the Dispose and rely on the GC. It's a waste of resources, plain and simple.

In .NET 4.0 I'd take a look at the Task Parallel Library, which is in the System.Threading assembly, new to 4.0.

Specifically, look at the Task class as well as the Wait method, which will allow you to specify a timeout.

Also, you will want to look at how to cancel a Task in the event it times out.

Plafker answered 28/5, 2010 at 15:48 Comment(0)
Z
3

Ugh, now that I looked a bit more at code samples using compiler-generated delegate.BeginInvoke I see that it returns IAsyncResult which has AsyncWaitHandle exactly for my goal:

var a = new Action(() => Thread.Sleep(1000)); // <- some looong action
IAsyncResult asyncResult = a.BeginInvoke(a.EndInvoke, null);
bool success = asyncResult.AsyncWaitHandle.WaitOne(500);

That wait handle in case of AsyncResult is in fact an instance of ManualResetEvent which is disposed automatically from thread-pool thread right when my asynchronous call completes.

Zalea answered 28/5, 2010 at 16:13 Comment(1)
Yup. But no real difference, only EndInvoke() can dispose it. Which you can't call if the wait timed-out. Don't bother, kernel events are dirt cheap and peanuts compared to that thread you kept running.Margaux

© 2022 - 2024 — McMap. All rights reserved.