Handling Asynchronous Timeouts

Hi All,

I'm developing a windows service in .Net to do some polling.  I was going to build it asynchronously to help performance and management of the threads, however I have a question about handling timeouts.

How do I handle a timeout using the begin invoke / end invoke?

I've seen the wait handles but not sure if that's what I want to be using.

Essentially the flow should look like:

call beginInvoke()
wait X seconds  (as defined by database)
handle timeout (if there is one)
call returns.

Links to coding examples or explanations would be great.

LVL 42
Kyle AbrahamsSenior .Net DeveloperAsked:
Who is Participating?
 
Todd GerbertIT ConsultantCommented:
In the button click event timer you have lines that start the ServiceHW and ServiceTimeOut methods on ThreadPool threads, and you are also calling RegisterWaitForSingleObject - but the WaitHandle's you're passing to RegisterWaitForSingleObject aren't associated with the ServiceHW or ServiceTimeOut methods.

Essentially, you're running the two worker threads, and the RegisterWaitForSingleObject calls are executing their methods every 5 seconds.  Basically, you're just using RegisterWaitForSingleObject as a timer that fires it's tick event every 5 seconds.

I think this is what you were trying to accomplish, though I don't think that you have any way to terminate the worker threads early - that is, even if their timed out callback fires, the thread is going to continue working.
using System;
using System.Collections.Generic;
using System.ComponentModel;
using System.Data;
using System.Drawing;
using System.Linq;
using System.Text;
using System.Windows.Forms;
using System.Threading;

namespace WindowsFormsApplication1
{
    public partial class Form1 : Form
    {
        delegate void SetTextDelegate(string text, TextBox tb);

        public Form1()
        {
            InitializeComponent();
        }

        private void SetText(string text, TextBox tb)
        {
            if (tb.InvokeRequired)
            {
                this.Invoke(new SetTextDelegate(SetText),
                    new object[] { text, tb });
            }
            else
                tb.Text = text;
        }

        private void PauseFiveSeconds(AutoResetEvent are)
        {
            // Simulate database work that takes 5 seconds
            Thread.Sleep(5000);
            are.Set();
        }

        private void PauseEightSeconds(AutoResetEvent are)
        {
            // Simulate database work that takes 8 seconds
            Thread.Sleep(8000);
            are.Set();
        }

        private void button1_Click(object sender, EventArgs e)
        {
            textBox1.Text = "Working...";
            textBox2.Text = "Working...";

            ThreadPool.QueueUserWorkItem(new WaitCallback(ThreadOneProc));
            ThreadPool.QueueUserWorkItem(new WaitCallback(ThreadTwoProc));
        }

        private void ThreadOneProc(Object stateInfo)
        {
            AutoResetEvent wh = new AutoResetEvent(false);
            RegisteredWaitHandle handle = ThreadPool.RegisterWaitForSingleObject(
                wh,
                new WaitOrTimerCallback(ThreadOneTimedOut),
                null,
                6000,
                true);
            
            PauseFiveSeconds(wh);
            
            handle.Unregister(wh);
        }

        private void ThreadTwoProc(Object stateInfo)
        {
            AutoResetEvent wh = new AutoResetEvent(false);
            RegisteredWaitHandle handle = ThreadPool.RegisterWaitForSingleObject(
                wh,
                new WaitOrTimerCallback(ThreadTwoTimedOut),
                null,
                6000,
                true);

            PauseEightSeconds(wh);

            handle.Unregister(wh);
        }

        private void ThreadOneTimedOut(Object state, bool timedOut)
        {
            SetTextDelegate st = new SetTextDelegate(SetText);
            if (timedOut)
                st("Timed Out", textBox1);
            else
                st("Completed", textBox1);
        }

        private void ThreadTwoTimedOut(Object state, bool timedOut)
        {
            SetTextDelegate st = new SetTextDelegate(SetText);
            if (timedOut)
                st("Timed Out", textBox2);
            else
                st("Completed", textBox2);
        }
    }
}

Open in new window

0
 
Stephan_SchrandtCommented:
Yes, AsyncWaitHandle is what you need:

string resp = string.Empty;        
YourDelegate dlg = new YourDelegate(YourProc);        
IAsyncResult res = dlg.BeginInvoke(yourvar,  new AsyncCallback(TransactionReturned), dlg);        
        if (!res.AsyncWaitHandle.WaitOne(timeoutMilliseconds, false))        
        {            
                response = "timed out";        
        }        
        else            
        {            
                response = dlg.EndInvoke(res);        
        }        
0
 
Kyle AbrahamsSenior .Net DeveloperAuthor Commented:
Won't the WaitOne block the thread though?  

What's the purpose of making the call asynchronously if you're going to block in the next line?
0
Cloud Class® Course: SQL Server Core 2016

This course will introduce you to SQL Server Core 2016, as well as teach you about SSMS, data tools, installation, server configuration, using Management Studio, and writing and executing queries.

 
Stephan_SchrandtCommented:
The desired use of waitone is to wait for completition and do some work in next lines before retrieving the result with endinvoke. I do not exactly know what you want to achieve, maybe these links will help you:

http://msdn.microsoft.com/en-us/library/2e08f6yc(VS.71).aspx?ppud=4
http://www.code-magazine.com/article.aspx?quickid=0305071&page=4
0
 
Kyle AbrahamsSenior .Net DeveloperAuthor Commented:
Hi Stephan,

Full scenario:

I have a bunch of rows returned from a database.  Each row contains information on what needs to be polled.

Essentially I'm looking to have a thread
get the next line to be polled
send the request with a callback for completion.
go on to the next request.

Request will eventually return with information to my callback that updates the DB.
If the request doesn't return in 10 seconds, update DB with an error . . . but the thread shouldn't need to wait for this?

My question is what if the request never completes?  I want to be able to get a flag if the request didn't complete in 10 seconds.  Is there a callback method for timeouts that can be used?  
0
 
Stephan_SchrandtCommented:
Yes there's also a way to implement a callback method. I copy it but please take a look at the links, they explain exactly the differences between various handling methods of asynchronous calls. In your case I would suggest using waitone in the thread that's inserting the rows. Because it's a thread you can go on with your other code.

Do for each line to insert
  BeginInvoke
  AsyncWaitHandle.WaitOne -> timeout = raisevent "error"
  EndInvoke
Loop
Raiseevent "finished"
add the event handlers in main to the thread  and run it. That will give you control of the flow.

Here's the code about callback method (taken from msdn)

public class AsyncMain {
    // Asynchronous method puts the thread id here.
    private static int threadId;

    static void Main(string[] args) {
        // Create an instance of the test class.
        AsyncDemo ad = new AsyncDemo();

        // Create the delegate.
        AsyncDelegate dlgt = new AsyncDelegate(ad.TestMethod);
   
        // Initiate the asychronous call.  Include an AsyncCallback
        // delegate representing the callback method, and the data
        // needed to call EndInvoke.
        IAsyncResult ar = dlgt.BeginInvoke(3000,
            out threadId,
            new AsyncCallback(CallbackMethod),
            dlgt );

        Console.WriteLine("Press Enter to close application.");
        Console.ReadLine();
    }
   
    // Callback method must have the same signature as the
    // AsyncCallback delegate.
    static void CallbackMethod(IAsyncResult ar) {
        // Retrieve the delegate.
        AsyncDelegate dlgt = (AsyncDelegate) ar.AsyncState;

        // Call EndInvoke to retrieve the results.
        string ret = dlgt.EndInvoke(out threadId, ar);

        Console.WriteLine("The call executed on thread {0}, with return value \"{1}\".", threadId, ret);
    }
}
0
 
Todd GerbertIT ConsultantCommented:
I don't believe BeginInvoke gives you a way to directly specify a timeout.  What I think you're asking for is to read some records from a database, start a new thread for each row, and handle the completion of the threads - knowing whether they finished or timed out?

I think you're going to have to keep track of the time yourself.  Does this simple example make any sense?
using System;
using System.Threading;

namespace ConsoleApplication1
{
	delegate void MyCallback(int id, bool TimedOut);

	// This class will be passed as thread param for each new thread
	class MyThreadParams
	{
		public int Iterations;
		public int Id;
		public MyCallback callback;
	}

	class Program
	{
		const int TIMEOUT = 5;
		private static long threadCount = 0;
		
		static void Main(string[] args)
		{
			// here the args[] array represents the rows from your database,
			// a new thread is started for each one
			for (int i = 0; i < args.Length; i++)
			{
				// Create a new param object to pass to the worker thread
				MyThreadParams param = new MyThreadParams();
				param.callback = new MyCallback(HandleCallback);
				param.Id = i;
				param.Iterations = Int32.Parse(args[i]);

				// Create a new worker thread, start it with the param object
				Thread newWorkerThread = new Thread(WorkerProc);
				newWorkerThread.Start(param);
				
				// Increment the number of worker thread count by 1
				Interlocked.Increment(ref threadCount);
			}

			// Continue doing stuff in your main thread
			// For this example, just waiting for all worker threads to finish
			while (Interlocked.Read(ref threadCount) > 0)
				Thread.Sleep(250);

			Console.WriteLine("Press any key to exit...");
			Console.ReadKey();
		}

		static void WorkerProc(object Param)
		{
			MyThreadParams threadParams = (MyThreadParams)Param; // Cast the Param object to a MyThreadParams object
			
			DateTime startTime = DateTime.Now; // Get the time this thread started
			

			Console.WriteLine("Worker with ID {0} started.", threadParams.Id);

			// do your database stuff with Async operations here
			// in this example the DB ops are simulated just by a Thread.Sleep(250)

			for (int i = 0; i < threadParams.Iterations; i++)
			{
				//Check if we'ev exceeded timeout
				if ((DateTime.Now - startTime).TotalSeconds >= TIMEOUT)
				{
					threadParams.callback(threadParams.Id, true);
					Interlocked.Decrement(ref threadCount);
					return;
				}

				Thread.Sleep(1000);
			}

			Interlocked.Decrement(ref threadCount);
			threadParams.callback(threadParams.Id, false);
			
		}

		static void HandleCallback(int id, bool TimedOut)
		{
			if (TimedOut)
				Console.WriteLine("Worker thread {0} timed out during execution.", id);
			else
				Console.WriteLine("Worker thread {0} completed successfully.", id);
		}
	}
}

Open in new window

0
 
Todd GerbertIT ConsultantCommented:
Here's a slightly simpler example using a delegate and BeginInvoke, but notice I'm still manually keeping track of how long the worker proc is running.
using System;
using System.Threading;

namespace ConsoleApplication1
{
	class WorkerReturnValue
	{
		public bool TimedOut;
		public int Id;

		public WorkerReturnValue(bool TimedOut, int Id)
		{
			this.TimedOut = TimedOut;
			this.Id = Id;
		}
	}

	class Program
	{
		delegate WorkerReturnValue WorkerProcDelegate(int Iterations, int Timeout, int Id);
		const int TIMEOUT = 5;
		static long WorkerCount = 0;

		static void Main(string[] args)
		{
			for (int i = 0; i < args.Length; i++)
			{
				WorkerProcDelegate worker = new WorkerProcDelegate(WorkerProc);

				worker.BeginInvoke(
					Int32.Parse(args[i]),
					TIMEOUT,
					i,
					HandleCallback,
					worker);

				Interlocked.Increment(ref WorkerCount);
			}

			while (Interlocked.Read(ref WorkerCount) > 0)
				Thread.Sleep(250);

			Console.WriteLine("All workers completed, press any key to exit.");
			Console.ReadKey();
		}

		static WorkerReturnValue WorkerProc(int Iterations, int Timeout, int Id)
		{
			// Do your db stuff here
			// For example will just loop, waiting 250milliseconds in each
			// iteration of the loop

			DateTime startTime = DateTime.Now;

			Console.WriteLine("Async call for id {0} started.", Id);

			for (int i = 0; i < Iterations; i++)
			{
				if ((DateTime.Now - startTime).TotalSeconds >= Timeout)
					return new WorkerReturnValue(true, Id);
				Thread.Sleep(250);
			}

			return new WorkerReturnValue(false, Id);
		}

		static void HandleCallback(IAsyncResult result)
		{
			WorkerProcDelegate theWorker = (WorkerProcDelegate)result.AsyncState;
			WorkerReturnValue returnValue = theWorker.EndInvoke(result);

			if (returnValue.TimedOut)
				Console.WriteLine("Worker with id {0} timed out.", returnValue.Id);
			else
				Console.WriteLine("Worker with id {0} returned successfully.", returnValue.Id);

			Interlocked.Decrement(ref WorkerCount);
		}
	}
}

Open in new window

0
 
Kyle AbrahamsSenior .Net DeveloperAuthor Commented:
Stephan:

using your wait one I came up with the following.  Note: Timeout.Service1 is a webservice that I'm invoking on my local.  (that is attached in the code section) however my timeout isn't working properly.  Not sure why the timeout isn't working.


public class Form1
{
    Timeout.Service1 proxy;
   
    public delegate void Call_Delegate();
       
    private void Button1_Click(System.Object sender, System.EventArgs e)
    {
       
       
        Call_Delegate callHW = default(Call_Delegate);
        callHW = ServiceHW;
       
        Call_Delegate callTimeout = default(Call_Delegate);
        callTimeout = ServiceTimeOut;
       
        IAsyncResult ar = default(IAsyncResult);
        IAsyncResult ar2 = default(IAsyncResult);
       
        TextBox1.Text = "Working";
       
       
        ar = callHW.BeginInvoke(null, null);
        ar.AsyncWaitHandle.WaitOne(5000, false);
       
        if (ar.IsCompleted) {
        }
        else {
            TextBox1.Text = "Timeout";
        }
        Application.DoEvents();
       
        TextBox2.Text = "Working";
       
        ar2 = callTimeout.BeginInvoke(null, null);
        ar2.AsyncWaitHandle.WaitOne(5000, false);
        if (ar2.IsCompleted) {
        }
        else {
            TextBox2.Text = "Timeout";
        }
           
           
        Application.DoEvents();
    }
   
    //Private Sub Async(ByVal result As IAsyncResult)
    // result.ToString()
   
    //End Sub
   
    private void TimeoutCallback(object sender, Timeout.TimeoutCompletedEventArgs args)
    {
        TextBox2.Text = args.Result();
    }
    private void HWCallback(object sender, Timeout.HelloWorldCompletedEventArgs args)
    {
        TextBox1.Text = args.Result();
    }
   
    private void ServiceHW()
    {
       
        proxy = new Timeout.Service1();
        proxy.TimeoutCompleted += TimeoutCallback;
        proxy.HelloWorldCompleted += HWCallback;
       
           
        proxy.HelloWorldAsync();
    }
   
    private void ServiceTimeOut()
    {
       
        proxy = new Timeout.Service1();
        proxy.TimeoutCompleted += TimeoutCallback;
        proxy.HelloWorldCompleted += HWCallback;
       
           
        proxy.TimeoutAsync();
    }
       
    private void Form1_Load(System.Object sender, System.EventArgs e)
    {
        System.Windows.Forms.Control.CheckForIllegalCrossThreadCalls = false;
    }
}


[WebMethod()] 
public string Timeout() 
{ 
    Thread.Sleep(10000); 
    return "10 second return"; 
} 
[WebMethod()] 
public string HelloWorld() 
{ 
      return "Hello World"; 
} 

Open in new window

0
 
Kyle AbrahamsSenior .Net DeveloperAuthor Commented:
Alright,

Got a lot of it working but have an issue now where it's reporting a timeout even after it responds.

I'm just using a windows form, 1 button 2 textboxes to step through the code but not sure why the timed_out() function is being called after a value is returned.

Any insights greatly appreciated.
using System.Threading; 

public class Form1 
{ 
    Timeout.Service1 proxy = new Timeout.Service1(); 
    
    public delegate void SetTextCallback(string s); 
    public delegate void SetTextCallback2(string t); 
    
    
    
    private void Button1_Click(System.Object sender, System.EventArgs e) 
    { 
        
        WaitHandle wh1 = new AutoResetEvent(false); 
        WaitHandle wh2 = new AutoResetEvent(false); 
        
        
        TextBox1.Text = "Working"; 
        TextBox2.Text = "Working"; 
        
        // ar = callHW.BeginInvoke(Nothing, Nothing) 
        
        ThreadPool.QueueUserWorkItem(ServiceHW); 
        ThreadPool.RegisterWaitForSingleObject(wh1, new WaitOrTimerCallback(timed_out), null, 5000, false); 
        
        
        ThreadPool.QueueUserWorkItem(ServiceTimeOut); 
            
            
        ThreadPool.RegisterWaitForSingleObject(wh2, new WaitOrTimerCallback(timed_out2), null, 5000, false); 
    } 
    
    private void timed_out() 
    { 
        SetTextCallback a = new SetTextCallback(SetText); 
        this.Invoke(a, new object[] { "Timed out" }); 
    } 
    
    
    private void timed_out2() 
    { 
        SetTextCallback2 d = new SetTextCallback2(SetText2); 
        this.Invoke(d, new object[] { "Timed out 2" }); 
    } 
    
    private void ServiceTimeOut() 
    { 
        
        proxy = new Timeout.Service1(); 
        proxy.TimeoutCompleted += TimeoutCallback; 
        
        //call async Timeout webservice function. 
            
        proxy.TimeoutAsync(); 
    } 
    
    private void TimeoutCallback(object sender, Timeout.TimeoutCompletedEventArgs args) 
    { 
        //END SERVICETIMEOUT() 
        SetTextCallback2 d = new SetTextCallback2(SetText2); 
        this.Invoke(d, new object[] { args.Result() }); 
    } 
    
    private void HWCallback(object sender, Timeout.HelloWorldCompletedEventArgs args) 
    { 
        //END SERVICE_HW 
        SetTextCallback d = new SetTextCallback(SetText); 
            
        this.Invoke(d, new object[] { args.Result() }); 
    } 
    
    private void ServiceHW() 
    { 
        
        proxy = new Timeout.Service1(); 
        // AddHandler proxy.TimeoutCompleted, AddressOf TimeoutCallback 
        proxy.HelloWorldCompleted += HWCallback; 
        
            
        proxy.HelloWorldAsync(); 
    } 
    
    
    private void SetText(string s) 
    { 
        this.TextBox1.Text = s; 
    } 
    
    private void SetText2(string t) 
    { 
        this.TextBox2.Text = t; 
    } 
} 

Open in new window

0
 
Kyle AbrahamsSenior .Net DeveloperAuthor Commented:
Thanks very much.  This sets a nice framework for what I'll be trying to accomplish.
0
 
Todd GerbertIT ConsultantCommented:
I still recommend my first suggestion...

In the example above put "MessageBox.Show("Hello World");" on line 82, just after "PauseEightSeconds(wh);"  Now when you run the code, after 6 seconds you'll see textBox2 filled with "Timed Out", but ThreadTwoProc continues to run, and when it finishes you'll still see it's message box.  In reality I think you might want the thead to stop and clean up nicely, so that when the second thread times out it stops, avoiding the "Hello World" message box?

Keep in mind that RegisterWaitForSingleObject and it's time out value apply to the AutoResetEvent you pass to it and whether that AutoResetEvent is signaled or not.  It does NOT apply to the thread itself, and whether or not the thread has completed.
0
Question has a verified solution.

Are you are experiencing a similar issue? Get a personalized answer when you ask a related question.

Have a better answer? Share it in a comment.

All Courses

From novice to tech pro — start learning today.