1

My C# application stops responding for a long time, as I break the Debug it stops on a function.

foreach (var item in list)
{
    xmldiff.Compare(item, secondary, output);
    ...
}

I guess the running time of this function is long or it hangs. Anyway, I want to wait for a certain time (e.g. 5 seconds) for the execution of this function, and if it exceeds this time, I skip it and go to the next item in the loop. How can I do it? I found some similar question but they are mostly for processes or asynchronous methods.

Ahmad
  • 8,811
  • 11
  • 76
  • 141

3 Answers3

3

You can do it the brutal way: spin up a thread to do the work, join it with timeout, then abort it, if the join didn't work.

Example:

var worker = new Thread( () => { xmlDiff.Compare(item, secondary, output); } );
worker.Start();
if (!worker.Join( TimeSpan.FromSeconds( 1 ) ))
    worker.Abort();

But be warned - aborting threads is not considered nice and can make your app unstable. If at all possible try to modify Compare to accept a CancellationToken to cancel the comparison.

Haukinger
  • 10,420
  • 2
  • 15
  • 28
  • Why down-vote? Is there any other non-cooperative method to cancel a method besides aborting the thread it runs in? – Haukinger Feb 14 '16 at 13:20
  • Thanks it seems working, I will accept it when I got sure – Ahmad Feb 14 '16 at 13:30
  • 1
    Aborting a thread can lead to corruption in the `AppDomain`. It should only ever be done when forceably shutting down an app. It is terrible to suggest this as a normal way to code. – Enigmativity Feb 15 '16 at 05:42
  • If it is the only solution to the problem at hand, it might be worth the risk. We don't know whether it is possible for him to move the processing in another process for example. BTW - when shutting down an app, it is better to kill the process, as that's guaranteed to work while `Thread.Abort` will only abort threads that are currently executing managed code. If the thread hangs in an unmanaged lib, it just does nothing. – Haukinger Feb 15 '16 at 08:14
0

I would avoid directly using threads and use Microsoft's Reactive Extensions (NuGet "Rx-Main") to abstract away the management of the threads.

I don't know the exact signature of xmldiff.Compare(item, secondary, output) but if I assume it produces an integer then I could do this with Rx:

var query =
    from item in list.ToObservable()
    from result in
        Observable
            .Start(() => xmldiff.Compare(item, secondary, output))
            .Timeout(TimeSpan.FromSeconds(5.0), Observable.Return(-1))
    select new { item, result };

var subscription =
    query
        .Subscribe(x =>
        {
            /* do something with `x.item` and/or `x.result` */
        });

This automatically iterates through each item and starts a background computation of xmldiff.Compare, but only allows each computation to take as much as 5.0 seconds before returning a default value of -1.

The subscription variable is an IDisposable, so if you want to abort the entire query before it completes just call .Dispose().

Enigmativity
  • 113,464
  • 11
  • 89
  • 172
0

I skip it and go to the next item in the loop

By "skip it", do you mean "leave it there" or "cancel it"? The two scenarios are quite different. But for both two I suggest you use Task.

//generate 10 example tasks
var tasks = Enumerable
    .Range(0, 10)
    .Select(n => new Task(() => DoSomething(n)))
    .ToList();

var maxExecutionTime = TimeSpan.FromSeconds(5);

foreach (var task in tasks)
{
    if (task.Wait(maxExecutionTime))
    {
        //the task is finished in time
    }
    else
    {
        // the task is over time
        // just leave it there
        // the loop continues
        // if you want to cancel it, see 
        // http://stackoverflow.com/questions/4783865/how-do-i-abort-cancel-tpl-tasks
    }
}

One thing to improve is "do you really need to run your tasks one by one?" If they are independent you can run them in parallel.

Cheng Chen
  • 42,509
  • 16
  • 113
  • 174