Is there a better waiting pattern for c#?

C#MultithreadingDesign Patterns

C# Problem Overview


I've found myself coding this type of thing a few times.

for (int i = 0; i < 10; i++)
{
   if (Thing.WaitingFor())
   {
      break;
   }
   Thread.Sleep(sleep_time);
}
if(!Thing.WaitingFor())
{
   throw new ItDidntHappenException();
}

It just looks like bad code, is there a better way of doing this / is it a symptom of bad design?

C# Solutions


Solution 1 - C#

A much better way to implement this pattern is to have your Thing object expose an event on which the consumer can wait. For example a ManualResetEvent or AutoResetEvent. This greatly simplifies your consumer code to be the following

if (!Thing.ManualResetEvent.WaitOne(sleep_time)) {
  throw new ItDidntHappen();
}

// It happened

The code on the Thing side is also not really any more complex.

public sealed class Thing {
  public readonly ManualResetEvent ManualResetEvent = new ManualResetEvent(false);

  private void TheAction() {
    ...
    // Done.  Signal the listeners
    ManualResetEvent.Set();
  }
}

Solution 2 - C#

Use events.

Have the thing you are waiting for raise an event when it's finished (or failed to finish within the allotted time) and then handle the event in your main application.

That way you don't have any Sleep loops.

Solution 3 - C#

A loop is not a TERRIBLE way to wait for something, if there's nothing else for your program to do while it waits (for instance while connecting to a DB). However, I see some issues with yours.

    //It's not apparent why you wait exactly 10 times for this thing to happen
    for (int i = 0; i < 10; i++)
    {
        //A method, to me, indicates significant code behind the scenes.
        //Could this be a property instead, or maybe a shared reference?
        if (Thing.WaitingFor()) 
        {
            break;
        }
        //Sleeping wastes time; the operation could finish halfway through your sleep. 
        //Unless you need the program to pause for exactly a certain time, consider
        //Thread.Yield().
        //Also, adjusting the timeout requires considering how many times you'll loop.
        Thread.Sleep(sleep_time);
    }
    if(!Thing.WaitingFor())
    {
        throw new ItDidntHappenException();
    }

In short, the above code looks more like a "retry loop", that's been bastardized to work more like a timeout. Here's how I would structure a timeout loop:

var complete = false;
var startTime = DateTime.Now;
var timeout = new TimeSpan(0,0,30); //a thirty-second timeout.

//We'll loop as many times as we have to; how we exit this loop is dependent only
//on whether it finished within 30 seconds or not.
while(!complete && DateTime.Now < startTime.Add(timeout))
{
   //A property indicating status; properties should be simpler in function than methods.
   //this one could even be a field.
   if(Thing.WereWaitingOnIsComplete)
   {
      complete = true;
      break;
   }
   
   //Signals the OS to suspend this thread and run any others that require CPU time.
   //the OS controls when we return, which will likely be far sooner than your Sleep().
   Thread.Yield();
}
//Reduce dependence on Thing using our local.
if(!complete) throw new TimeoutException();

Solution 4 - C#

If possible, have the asynchronous processing wrapped in a Task<T>. This provides the best of all worlds:

  • You can respond to the completion in an event-like way by using task continuations.
  • You can wait using the completion's waitable handle because Task<T> implements IAsyncResult.
  • Tasks are easily composable using the Async CTP; they also play well with Rx.
  • Tasks have a very clean built-in exception handling system (in particular, they correctly preserve the stack trace).

If you need to use a timeout, then Rx or the Async CTP can provide that.

Solution 5 - C#

I would take a look at the WaitHandle class. Specifically the ManualResetEvent class that waits until the object is set. You can also specify timeout values for it and check if it was set afterward.

// Member variable
ManualResetEvent manual = new ManualResetEvent(false); // Not set

// Where you want to wait.
manual.WaitOne(); // Wait for manual.Set() to be called to continue here
if(!manual.WaitOne(0)) // Check if set
{
   throw new ItDidntHappenException();
}

Solution 6 - C#

A call to Thread.Sleep always is an active wait which should be avoided.
One alternative would be to use a timer. For easier usage, you could encapsulate that into a class.

Solution 7 - C#

I usually discourage throwing exceptions.

// Inside a method...
checks=0;
while(!Thing.WaitingFor() && ++checks<10) {
    Thread.Sleep(sleep_time);
}
return checks<10; //False = We didn't find it, true = we did

Solution 8 - C#

I think you should use AutoResetEvents. They work great when you are waiting for another thread to finish it's task

Example:

AutoResetEvent hasItem;
AutoResetEvent doneWithItem;
int jobitem;

public void ThreadOne()
{
 int i;
 while(true)
  {
  //SomeLongJob
  i++;
  jobitem = i;
  hasItem.Set();
  doneWithItem.WaitOne();
  }
}

public void ThreadTwo()
{
 while(true)
 {
  hasItem.WaitOne();
  ProcessItem(jobitem);
  doneWithItem.Set();
  
 }
}

Solution 9 - C#

Here is how you can do it with System.Threading.Tasks:

Task t = Task.Factory.StartNew(
	() =>
	{
		Thread.Sleep(1000);
	});
if (t.Wait(500))
{
	Console.WriteLine("Success.");
}
else
{
	Console.WriteLine("Timeout.");
}

But if you can't use Tasks for some reason (like a requirement of .Net 2.0) then you can use ManualResetEvent as mentioned in JaredPar's answer or use something like this:

public class RunHelper
{
	private readonly object _gate = new object();
	private bool _finished;
	public RunHelper(Action action)
	{
		ThreadPool.QueueUserWorkItem(
			s =>
			{
				action();
				lock (_gate)
				{
					_finished = true;
					Monitor.Pulse(_gate);
				}
			});
	}

	public bool Wait(int milliseconds)
	{
		lock (_gate)
		{
			if (_finished)
			{
				return true;
			}

			return Monitor.Wait(_gate, milliseconds);
		}
	}
}

With the Wait/Pulse approach you don't explicitly create Events so you don't need to care about disposing them.

Usage example:

var rh = new RunHelper(
	() =>
	{
		Thread.Sleep(1000);
	});
if (rh.Wait(500))
{
	Console.WriteLine("Success.");
}
else
{
	Console.WriteLine("Timeout.");
}

Attributions

All content for this solution is sourced from the original question on Stackoverflow.

The content on this page is licensed under the Attribution-ShareAlike 4.0 International (CC BY-SA 4.0) license.

Content TypeOriginal AuthorOriginal Content on Stackoverflow
Questionprobably at the beachView Question on Stackoverflow
Solution 1 - C#JaredParView Answer on Stackoverflow
Solution 2 - C#ChrisFView Answer on Stackoverflow
Solution 3 - C#KeithSView Answer on Stackoverflow
Solution 4 - C#Stephen ClearyView Answer on Stackoverflow
Solution 5 - C#SwDevMan81View Answer on Stackoverflow
Solution 6 - C#Daniel HilgarthView Answer on Stackoverflow
Solution 7 - C#IshpeckView Answer on Stackoverflow
Solution 8 - C#Kornél RegiusView Answer on Stackoverflow
Solution 9 - C#GebbView Answer on Stackoverflow