C# Thread won't sleep?

C#.NetMultithreading

C# Problem Overview


I have this code :

void Main()
{
	System.Timers.Timer t = new System.Timers.Timer (1000);
	t.Enabled=true;
	t.Elapsed+= (sender, args) =>c();
	Console.ReadLine();
	
}

int h=0;
public void c()
{
    h++;
    new Thread(() => doWork(h)).Start();
}

public void doWork(int h)
{
    Thread.Sleep(3000);
    h.Dump();
}

I wanted to see what happens if the interval is 1000 ms and the job process is 3000 ms.

However I saw a strange behavior - the 3000 ms delay occurs only at the start !

How can I make each doWork sleep 3000 ms?

As you can see here, at the beginning there is a 3 second delay, and then it iterates 1 second each.

enter image description here

C# Solutions


Solution 1 - C#

Every time the timer ticks, you start a thread to do some sleeping; that thread is completely isolated, and the timer is going to keep on firing every second. Actually, the timer fires every second even if you move the Sleep(3000) into c().

What you have currently is:

1000 tick (start thread A)
2000 tick (start thread B)
3000 tick (start thread C)
4000 tick (start thread D, A prints line)
5000 tick (start thread E, B prints line)
6000 tick (start thread F, C prints line)
7000 tick (start thread G, D prints line)
8000 tick (start thread H, E prints line)
...

It is unclear what you are trying to do. You could disable the timer when you don't want it firing, and resume it again once ready, but it is unclear what the purpose of the Sleep() is here. Another option is just a while loop with a Sleep() in it. Simple, and doesn't involve lots of threads.

Solution 2 - C#

Every second you're starting new thread with 3 sec delay. It happens like this:

  1. thread 1 start
  2. thread 2 start, thread 1 sleeps
  3. thread 3 start, thread 2 sleeps, thread 1 sleeps
  4. thread 4 start, thread 3 sleeps, thread 2 sleeps, thread 1 sleeps
  5. thread 5 start, thread 4 sleeps, thread 3 sleeps, thread 2 sleeps, thread 1 dumps
  6. thread 6 start, thread 5 sleeps, thread 4 sleeps, thread 3 sleeps, thread 2 dumps
  7. thread 7 start, thread 6 sleeps, thread 5 sleeps, thread 4 sleeps, thread 3 dumps

As you can see, each thread sleeps for 3 seconds, yet a dump occurs every second.

How do one works with threads? smth like this:

void Main()
{
    new Thread(() => doWork()).Start();
    Console.ReadLine();
}

public void doWork()
{
    int h = 0;
    do
    {
        Thread.Sleep(3000);
        h.Dump();
        h++;
    }while(true);
}

Solution 3 - C#

Your example is very interesting - it shows the side effects of parallel processing. To answer your question, and to make it easier to see the side effects, I've slightly modified your example:

using System;
using System.Threading;
using System.Diagnostics;

public class Program
{
	public static void Main()
	{
		(new Example()).Main();
	}
}

public class Example
{
	public void Main()
	{
		System.Timers.Timer t = new System.Timers.Timer(10);
		t.Enabled = true;
		t.Elapsed += (sender, args) => c();
		Console.ReadLine(); t.Enabled = false;
	}

	int t = 0;
	int h = 0;
	public void c()
	{
		h++;
		new Thread(() => doWork(h)).Start();
	}

	public void doWork(int h2)
	{
		Stopwatch sw = new Stopwatch();
		sw.Start();
		try
		{
			t++;
			Console.WriteLine("h={0}, h2={1}, threads={2} [start]", h, h2, t);
			Thread.Sleep(3000);
		}
		finally
		{
			sw.Stop();
			var tim = sw.Elapsed;
			var elapsedMS = tim.Seconds * 1000 + tim.Milliseconds;
			t--;
			Console.WriteLine("h={0}, h2={1}, threads={2} [end, sleep time={3} ms] ", h, h2, t, elapsedMS);
		}
	}
}

What I've modified here is the following:

  • Timer interval is now 10 ms, the threads still have 3000 ms. The effect is that while threads are sleeping, new threads will be created
  • I've added varialbe t, which counts the number of threads currently being active (it is increased when the thread starts and decreased right before the thread ends)
  • I've added 2 dump statements, printing out the thread start and the thread end
  • Finally, I've given the parameter of function doWork a different name (h2), which allows to see the value of the underlying variable h

Now it is intersting to see the output of this modified program in LinqPad (note the values are not always the same as they're depending on the race conditions of the started threads):

	h=1, h2=1, threads=1 [start]
	h=2, h2=2, threads=2 [start]
	h=3, h2=3, threads=3 [start]
	h=4, h2=4, threads=4 [start]
	h=5, h2=5, threads=5 [start]
	...
	h=190, h2=190, threads=190 [start]
	h=191, h2=191, threads=191 [start]
	h=192, h2=192, threads=192 [start]
	h=193, h2=193, threads=193 [start]
	h=194, h2=194, threads=194 [start]
	h=194, h2=2, threads=192 [end]
	h=194, h2=1, threads=192 [end]
	h=194, h2=3, threads=191 [end]
	h=195, h2=195, threads=192 [start]

I think the values speak for themselves: What is happening is that every 10 ms a new thread is started, while others are still sleeping. Also interesting is to see that h is not always equal to h2, especially not if more threads are started while others are sleeping. The number of threads (variable t) is after a while stabilizing, i.e. running around 190-194.

You might argue, that we need to put locks on the variables t and h, for example

readonly object o1 = new object(); 
int _t=0; 
int t {
	   get {int tmp=0; lock(o1) { tmp=_t; } return tmp; } 
	   set {lock(o1) { _t=value; }} 
	  }

While that is a cleaner approach, it didn't change the effect shown in this example.

Now, in order to prove that each thread really sleeps 3000ms (= 3s), let's add a Stopwatch to the worker thread doWork:

public void doWork(int h2) 
{ 
	Stopwatch sw = new Stopwatch(); sw.Start();
	try 
	{
		t++; string.Format("h={0}, h2={1}, threads={2} [start]", 
							h, h2, t).Dump();								
		Thread.Sleep(3000); 		}
	finally {
		sw.Stop(); var tim = sw.Elapsed;
		var elapsedMS = tim.Seconds*1000+tim.Milliseconds;
		t--; string.Format("h={0}, h2={1}, threads={2} [end, sleep time={3} ms] ", 
							h, h2, t, elapsedMS).Dump();
	}
} 

For a proper cleanup of the threads, let's disable the timer after the ReadLine as follows:

	Console.ReadLine(); t.Enabled=false; 

This allows you to see what happens if no more threads are starting, after you've pressed ENTER:

	...
	h=563, h2=559, threads=5 [end, sleep time=3105 ms] 
	h=563, h2=561, threads=4 [end, sleep time=3073 ms] 
	h=563, h2=558, threads=3 [end, sleep time=3117 ms] 
	h=563, h2=560, threads=2 [end, sleep time=3085 ms] 
	h=563, h2=562, threads=1 [end, sleep time=3054 ms] 
	h=563, h2=563, threads=0 [end, sleep time=3053 ms] 

You can see they are all being terminated one after the other as expected and they slept about 3s (or 3000ms).

Solution 4 - C#

The reason you see this behavior is simple: you schedule a new thread each second, with the result becoming visible three seconds later. You do not see anything for the first four seconds; then, the thread that has been started three seconds ago dumps; another thread will have been sleeping for two seconds by then, and yet another - for one second. The next second thread #2 dumps; then thread #3, #4, and so on - you get a printout every second.

If you would like to see a printout every three seconds, you should schedule a new thread every three seconds with any delay that you would like: the initial thread will output in three seconds plus the delay; all the subsequent threads will be firing on three-second intervals.

Solution 5 - C#

Seems like you are runing a new thread every second wich is not a good idea, use backgroundworker, and when the event backgroundworker completed call the C function again, that way you wont need a timer

Solution 6 - C#

Each doWork is sleeping for three seconds, but their sleeps overlap because you create the threads at one second intervals.

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
QuestionRoyi NamirView Question on Stackoverflow
Solution 1 - C#Marc GravellView Answer on Stackoverflow
Solution 2 - C#Agent_LView Answer on Stackoverflow
Solution 3 - C#MattView Answer on Stackoverflow
Solution 4 - C#Sergey KalinichenkoView Answer on Stackoverflow
Solution 5 - C#JohnnBladeView Answer on Stackoverflow
Solution 6 - C#Chris DicksonView Answer on Stackoverflow