다음을 통해 공유


Timeout a function call in C#

There are some scenarios when you can't afford to allow any long running external/internal method to take forever and eat up your processing resource. You should time it out if it takes longer than X units.

 There are several ways to do that. I like the way using cancellation token the most.

 Here we are calling GetResult() method which might take longer than 5000ms.Code snippet:

  int QueryTimeOut = 5000; //in ms
 var tokenSource = new CancellationTokenSource();
 CancellationToken token = tokenSource.Token;
 var task = Task.Factory.StartNew(() => queryRun.GetResult(), token);
 timedOut = false;
 
 if (!task.Wait(QueryTimeOut, token))
 {
 timedOut = true;
 message = String.Format(" Method Timed out");
 tokenSource.Cancel();
 return -1;
 }
 Int32 result = Convert.ToInt32(task.Result);
 task.Dispose();
 return result;
 

NOTE: Above code does not kill the GetResult() which still runs in background. To Abort processing of GetResult/ long running method, please see below snippet:

  using System;
 using System.Threading;
 using System.Collections.Generic;
 using System.IO;
 using System.Threading.Tasks;
 
 namespace n
 {
 public class MonitorSample
 {
 
 private static int GetResult()
 {
 int result = 0;
 while (++result < 500)
 {
 Console.WriteLine(result);
 Thread.Sleep(5);
 }
 return result;
 }
 public static void Main(String[] args)
 {
 int QueryTimeOut = 50; //in ms
 int result = 0;
 Boolean timeOut = false;
 ManualResetEvent wait = new ManualResetEvent(false);
 Thread work = new Thread(new ThreadStart(() =>
 {
 //some long running method requiring synchronization
 result = GetResult();
 wait.Set();
 }));
 work.Start();
 Boolean signal = wait.WaitOne(QueryTimeOut);
 if (!signal) {
 work.Abort();
 timeOut = true; 
 }
 Console.WriteLine(String.Format("Result = {0}, timedout = {1}", result, timeOut));
 Console.ReadKey();
 }
 }
 }
 

Comments

  • Anonymous
    March 28, 2014
    The comment has been removed

  • Anonymous
    March 28, 2014
    Yes Phil, I should include that inside task.Wait() if block.

  • Anonymous
    April 01, 2014
    The comment has been removed

  • Anonymous
    April 02, 2014
    #Marcel, I have verified this code, tokenSource.Cancel() is responsible to cancel the task once the timeout value is reached. After this the background task is cancelled and will not be completed. Can you please run the following code, change the QueryTimeout value and observe the behavior of the program. using System; using System.Threading; using System.Collections.Generic; using System.IO; using System.Threading.Tasks; namespace n{    public class MonitorSample    {        private static Int32 GetResult()        {            Thread.Sleep(5);            return 3;        }        public static void Main(String[] args)        {            int QueryTimeOut = 50; //in ms            var tokenSource = new CancellationTokenSource();            CancellationToken token = tokenSource.Token;            Int32 result = 0;            Boolean timeout = false;            var task = Task.Factory.StartNew(() => GetResult(), token);            if (!task.Wait(QueryTimeOut))            {                timeout = true;                tokenSource.Cancel();            }            else            {                result = Convert.ToInt32(task.Result);                task.Dispose();            }            Console.WriteLine(String.Format("Result = {0}, timedout = {1}",result, timeout));            Console.ReadKey();        }    } } Let me know if you have further comments.

  • Anonymous
    April 02, 2014
    A slight change to your GetResult method will illustrate the problem: private static int GetResult() {    int result = 0;    while (++result < 500)    {        Console.WriteLine(result);        Thread.Sleep(5);    }    return result; } When you see the "timedout" message, wait for a few seconds; you'll see the messages from the GetResult method are still being written to the console. The task has been cancelled, but the method is still running.

  • Anonymous
    April 02, 2014
    My GetResult() is an external method. I cannot pass token to the method. Do we have alternatives to stop the processing?

  • Anonymous
    April 02, 2014
    Thanks Richard and Marcel for enlightening me.  I tried following code and works well. This is using thread and without cancellation token. using System; using System.Threading; using System.Collections.Generic; using System.IO; using System.Threading.Tasks; namespace n {    public class MonitorSample    {        private static int GetResult()        {            int result = 0;            while (++result < 500)            {                Console.WriteLine(result);                Thread.Sleep(5);            }            return result;        }        public static void Main(String[] args)        {            int QueryTimeOut = 50; //in ms            int result = 0;            Boolean timeOut = false;            ManualResetEvent wait = new ManualResetEvent(false);            Thread work = new Thread(new ThreadStart(() =>            {                //some long running method requiring synchronization                result = GetResult();                wait.Set();            }));            work.Start();            wait.WaitOne(QueryTimeOut);            if (work.IsAlive) {                work.Abort();                timeOut = true;            }            Console.WriteLine(String.Format("Result = {0}, timedout = {1}", result, timeOut));            Console.ReadKey();        }    } }