?
Solved

Handling Asynchronous Timeouts

Posted on 2009-12-28
12
Medium Priority
?
685 Views
Last Modified: 2013-12-03
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.

0
Comment
Question by:Kyle Abrahams
  • 5
  • 4
  • 3
12 Comments
 
LVL 9

Expert Comment

by:Stephan_Schrandt
ID: 26131006
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
 
LVL 41

Author Comment

by:Kyle Abrahams
ID: 26131158
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
 
LVL 9

Assisted Solution

by:Stephan_Schrandt
Stephan_Schrandt earned 600 total points
ID: 26131332
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
Free Backup Tool for VMware and Hyper-V

Restore full virtual machine or individual guest files from 19 common file systems directly from the backup file. Schedule VM backups with PowerShell scripts. Set desired time, lean back and let the script to notify you via email upon completion.  

 
LVL 41

Author Comment

by:Kyle Abrahams
ID: 26131630
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
 
LVL 9

Expert Comment

by:Stephan_Schrandt
ID: 26131799
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
 
LVL 33

Expert Comment

by:Todd Gerbert
ID: 26132079
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
 
LVL 33

Expert Comment

by:Todd Gerbert
ID: 26132268
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
 
LVL 41

Author Comment

by:Kyle Abrahams
ID: 26133518
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
 
LVL 41

Author Comment

by:Kyle Abrahams
ID: 26134353
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
 
LVL 33

Accepted Solution

by:
Todd Gerbert earned 1400 total points
ID: 26135247
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
 
LVL 41

Author Closing Comment

by:Kyle Abrahams
ID: 31670375
Thanks very much.  This sets a nice framework for what I'll be trying to accomplish.
0
 
LVL 33

Expert Comment

by:Todd Gerbert
ID: 26138048
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

Featured Post

How to Use the Help Bell

Need to boost the visibility of your question for solutions? Use the Experts Exchange Help Bell to confirm priority levels and contact subject-matter experts for question attention.  Check out this how-to article for more information.

Question has a verified solution.

If you are experiencing a similar issue, please ask a related question

A long time ago (May 2011), I have written an article showing you how to create a DLL using Visual Studio 2005 to be hosted in SQL Server 2005. That was valid at that time and it is still valid if you are still using these versions. You can still re…
Exception Handling is in the core of any application that is able to dignify its name. In this article, I'll guide you through the process of writing a DRY (Don't Repeat Yourself) Exception Handling mechanism, using Aspect Oriented Programming.
This is Part 3 in a 3-part series on Experts Exchange to discuss error handling in VBA code written for Excel. Part 1 of this series discussed basic error handling code using VBA. http://www.experts-exchange.com/videos/1478/Excel-Error-Handlin…
Look below the covers at a subform control , and the form that is inside it. Explore properties and see how easy it is to aggregate, get statistics, and synchronize results for your data. A Microsoft Access subform is used to show relevant calcul…

850 members asked questions and received personalized solutions in the past 7 days.

Join the community of 500,000 technology professionals and ask your questions.

Join & Ask a Question