Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Stopping a method after a specified time period

I'm running parallel operations on a list of PCs I'm getting from the Active Directory. I was using this method to check PC states, such as whether the computer was online, or if a specific directory existed. However due to the nature of these, sometimes slow, operations, I wanted to include a timeout so my application could continue to operate.

public static T MethodTimeout<T>(Func<T> f, int timeout, out bool completed)
{
    T result = default(T);
    var thread = new Thread(() => result = F());
    thread.Start();
    Completed = thread.Join(Timeout);
    if (!Completed) thread.Abort();
    return result;
}

This works for the most part, but the processing usage seemed to spike a little, and in a few cases I ran into Out of Memory exceptions. So, I since changed the method to use tasks in the hope that the ThreadPool would eliminate the aforementioned problems:

public static T MethodTimeout<T>(Func<T> f, int timeout, out bool completed)
{
    T result = default(T);

    var timedTask = Task.Factory.StartNew(() => result = F());
    Completed = timedTask.Wait(Timeout);

    return result;
}

However, I have a feeling I'm just filling up the ThreadPool with processes that are hanging waiting for these potentially long tasks to finish. Since I'm passing the task's function as a parameter I don't see a way to use a cancellation token. However, my experience is very limited with these classes and I might be missing some superior techniques.

Here's how I've been using the method above:

bool isReachable; // Check if the directory exists (1 second)
MethodTimeout(() => Directory.Exists(startDirectory), 1000, out isReachable);

A quick note, I'm only running the above check after I've already confirmed the PC is online via a WMI call (also using MethodTimeout). I'm aware through my early testing that checking the directory before that ends up being inefficient.

I'm also open to replacing this approach with something better.

like image 206
Parrish Husband Avatar asked Oct 31 '13 23:10

Parrish Husband


People also ask

How to stop a method after a certain time in Java?

Using a Loop long start = System. currentTimeMillis(); long end = start + 30 * 1000; while (System. currentTimeMillis() < end) { // Some expensive operation on the item. } Here, the loop will break if the time has surpassed the limit of 30 seconds.

How do you stop a run method?

Whenever we want to stop a thread from running state by calling stop() method of Thread class in Java. This method stops the execution of a running thread and removes it from the waiting threads pool and garbage collected. A thread will also move to the dead state automatically when it reaches the end of its method.

How do you stop a program from few seconds in Java?

The easiest way to delay a java program is by using Thread. sleep() method. The sleep() method is present in the Thread class. It simply pauses the current thread to sleep for a specific time.


2 Answers

I may be the harbinger of bad news here, but this scenario is way more difficult to deal with than most people think. It seems that you are already picking up on this. Using the cooperative cancellation mechanisms is all good, but you have to be able to actually cancel the time consuming operation to be able to use them effectively. The problem is that Directory.Exists can not be cancelled.

The problem with your first solution is that you are aborting a thread. This is not a good idea because it halts the thread at unpredictable points. This may lead to the corruption of data structures for anything that was executing on the call stack when the abort got injected. In this particular case I would not be surprised if the Thread.Abort call actually hung. The reason is that aborts are normally delayed while the execution is in unmanaged code. It is likely Directory.Exists defers to an unmanaged module. If that is the case then the abort will not work anyway.

The problem with the second solution is that you are leaving the task orphaned. That Directory.Exists call would still be executing on a thread pool thread somewhere. This is because you have not actually cancelled it.

To be honest I am not really sure what to do about this. The lack of a cancellable Directory.Exists method is very problematic. My first thought is to attempt a write or read from the directory you want to test as kind of a proxy for checking its existence. The FileStream class does have cancellable operations. In fact, many of the methods even accept a CancellationToken as a parameter. Or you could close the FileStream and that will cancel any pending operations as well. Another option might be to use the Win32 API functions to do the IO. Then you could call CancelSynchronousIo if necessary.

I know this is not much of an answer because all I have really done is tell you what you cannot do, but did not offer a definitive solution. The point is that the best solution starts with a cancellable operation. It is unfortunate that some of the BCL classes do not provide these even when they should.

like image 199
Brian Gideon Avatar answered Nov 15 '22 20:11

Brian Gideon


If you are using .Net 4.5 you can do it using CancelAfter:

var cts = new CancellationTokenSource(3000); // Set timeout

Task.Run(() =>
{
    while (!cts.Token.IsCancellationRequested)
    {
        // Doing Work...
    }

}, cts.Token);
like image 25
Damian Drygiel Avatar answered Nov 15 '22 20:11

Damian Drygiel