The method stops after a specified period of time

I perform parallel operations on the list of computers that I get from ActiveDirectory. I used this method to check the status of a PC, for example, if the computer was online, or if a specific directory exists. However, due to the nature of these sometimes slow operations, I wanted to enable a timeout so that my application could continue.

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 use of processing seems to have increased slightly, and in some cases I have run into memory exceptions. Therefore, I have since changed the way we use tasks in the hope that ThreadPool will fix the above 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 that I am just populating ThreadPool with processes that are hanging up, waiting for the completion of these potentially long tasks. Since I pass the task function as a parameter, I see no way to use the cancel token, however my experience is very limited to these classes, and I could skip some excellent methods.

This is how I used the method above:

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

Quick note. I perform the above verification only after I have already confirmed that the computer is connected through a WMI call (also using MethodTimeout). I am well aware of my early testing that checking a directory before this becomes incredibly inefficient.

I, too, of course, am open to disrupt my approach with something better. My loyalty is in the number of operations per second, and not just in what I could come up with.

+8
multithreading c # task-parallel-library active-directory
source share
3 answers

I may be the harbinger of bad news here, but this scenario is much harder to handle than many people think. It seems you are already typing this. Using collaborative cancellation mechanisms is all well and good, but you should be able to actually cancel the time-consuming operation in order to be able to use them effectively. The problem is that Directory.Exists cannot be undone.

The problem with your first solution is that you are interrupting the thread. This is not a good idea because it stops the flow at unpredictable points. This can damage data structures for everything that was running on the call stack when the interrupt was canceled. In this particular case, I would not be surprised if the call to Thread.Abort really hung. The reason is that interrupts are usually delayed, and execution is performed in unmanaged code. Directory.Exists likely to overload the unmanaged module. If so, then the interrupt will not work.

The problem with the second solution is that you leave the task an orphan. This call to Directory.Exists will still be executed on the thread pool thread. This is because you have not actually canceled it.

Honestly, I'm not quite sure what to do with this. The lack of a cancellation method for Directory.Exists very problematic. My first thought is to try to write or read from the directory you want to test as a kind of proxy to verify its existence. The FileStream class has undo operations. In fact, many of the methods even accept the CancellationToken parameter as a parameter. Or you can close the FileStream and cancel any pending operations. Another option would be to use the Win32 API functions to enter IO. You can then call CancelSynchronousIo if necessary.

I know that this is not a very important answer, because all I really did was tell you that you canโ€™t do it, but you havenโ€™t offered a final solution. The fact is that the best solution starts with the cancellation operation. Unfortunately, some BCL classes do not provide them, even if they should.

+6
source share

If you are using .Net 4.5, you can do this with CancelAfter :

 var cts = new CancellationTokenSource(3000); // Set timeout Task.Run(() => { while (!cts.Token.IsCancellationRequested) { // Doing Work... } }, cts.Token); 
+4
source share

Could something like this work? You will need to require the consumer to pass you a Func that accepts the CancellationTokenSource . That Func will be responsible for checking IsCancellationRequested in the appropriate place in its code ...

 public static T MethodTimeout<T>(Func<T, CancellationTokenSource> F, int Timeout, out bool Completed) { T result = default(T); var source = new CancellationTokenSource(Timeout); var timedTask = Task.Factory.StartNew(() => result = F(source)); Completed = timedTask.Wait(Timeout); if(!Completed) source.Cancel(); return result; } 

My knowledge of TPL is insignificant, so sorry if my code is not quite right. I have no way to test it with my old version of VS. Fixed / fixed greetings :)

0
source share

All Articles