ThreadPool uses excessive amounts of memory in just a few seconds - c#

I have made a simple console application for printing prime numbers. I am using ThreadPool for the function which checks whether a number is prime or not.
In the task manager , this program starts to take too much memory ( 1 GB in a few seconds )
How do I improve that if I have to still use ThreadPool?
Here is the code which I wrote
class Program
{
static void Main(string[] args)
{
Console.WriteLine(2);
Console.WriteLine(3);
Console.WriteLine(5);
Console.WriteLine(7);
Console.WriteLine(11);
Console.WriteLine(13);
Console.WriteLine(17);
for (long i = 19; i < Int64.MaxValue; i = i+2)
{
if(i % 3 == 0 || i % 5 == 0 || i % 7 == 0 || i % 11 == 0 || i % 13 == 0 || i % 17 == 0 )
continue;
ThreadPool.QueueUserWorkItem(CheckForPrime, i);
}
Console.Read();
}
private static void CheckForPrime(object i)
{
var i1 = i as long?;
var val = Math.Sqrt(i1.Value);
for (long j = 19; j <= val; j = j + 2)
{
if (i1 % j == 0) return;
}
Console.WriteLine(i1);
}
}

Simplest way to fix your code, just limit the work queue using a semaphore;
class Program
{
// Max 100 items in queue
private static readonly Semaphore WorkLimiter = new Semaphore(100, 100);
static void Main(string[] args)
{
Console.WriteLine(2);
Console.WriteLine(3);
Console.WriteLine(5);
Console.WriteLine(7);
Console.WriteLine(11);
Console.WriteLine(13);
Console.WriteLine(17);
for (long i = 19; i < Int64.MaxValue; i = i + 2)
{
if (i % 3 == 0 || i % 5 == 0 || i % 7 == 0 || i % 11 == 0 || i % 13 == 0 || i % 17 == 0)
continue;
// Get one of the 100 "allowances" to add to the queue.
WorkLimiter.WaitOne();
ThreadPool.QueueUserWorkItem(CheckForPrime, i);
}
Console.Read();
}
private static void CheckForPrime(object i)
{
var i1 = i as long?;
try
{
var val = Math.Sqrt(i1.Value);
for (long j = 19; j <= val; j = j + 2)
{
if (i1%j == 0) return;
}
Console.WriteLine(i1);
}
finally
{
// Allow another add to the queue
WorkLimiter.Release();
}
}
}
This will allow you to keep the queue full (100 items in queue) at all times, without over-filling it or adding a Sleep.

To put it rather bluntly, you're doing multi-threading wrong. Threads are a powerful tool when used correctly, but like all tools, they're not the correct solution in every case. A glass bottle works well for holding beer, but not so great for hammering nails.
In the general case, creating more threads is not going to make things run any faster, and that is particularly true here as you've discovered. The code you've written queues up a new thread each iteration through your loop, and each of those threads will allocate a stack. Since the default size for a stack in the .NET world is 1 MB, it doesn't take very long for your memory commitment to skyrocket. It therefore comes as no particular surprise that you're exceeding 1 GB. Eventually, you'll run into a hard memory limit and get an OutOfMemoryException thrown at you. And memory is just the most obvious of the resources that your design is quickly starving your system for. Unless your system resources can grow exponentially with your thread pool, you're not going to experience any performance advantages.
Adil suggests inserting a call to Thread.Sleep to give the new thread you create time to run before continuing through the loop (and creating additional threads). As I mentioned in a comment, although this "works", it seems like an awfully ugly hack to me. But it's hard for me to suggest a better solution because the real problem is the design. You say that you have to use a thread pool, but you don't say why that is the case.
If you absolutely have to use a thread pool, the best workaround is probably to set an arbitrary limit on the size of the thread pool (i.e., how many new threads it can spawn), which is accomplished by calling the SetMaxThreads method. This feels at least a little less hacky to me than Thread.Sleep.
Note: If you decide to pursue the SetMaxThreads approach, you should be aware that you cannot set the maximum to less than the minimum. The default value for the minimum is the number of CPU cores, so if you have a dual-core processor, you can't set the maximum to 1 without first lowering the minimum.
Finally, although it doesn't really change the answer in this case, it is worth noting that Task Manager is not a memory profiler. Relying on it as if it were one will frequently get you bad (or at least very misleading) data.
Edit: After further thought, it occurs to me that the problem really lies not with exponential execution but with exponential querying. The maximum number of allowed threads is probably irrelevant because the code will still queue 'em up faster than they can ever hope to be processed. So never mind about limiting the size. You probably want to go with Joachim's solution involving the creation of a semaphore, or the implicit suggestion that everyone has made of not using a thread pool.

You are creating threads in loop without any break. You should give some break to the process of creating thread so that some of thread finish their execution before you create more thread in the ThreadPool. You can use System.Threading.Thread.Sleep for that.
for (long i = 19; i < Int64.MaxValue; i = i+2)
{
if(i % 3 == 0 || i % 5 == 0 || i % 7 == 0 || i % 11 == 0 || i % 13 == 0 || i % 17 == 0 )
continue;
ThreadPool.QueueUserWorkItem(CheckForPrime, i);
System.Threading.Thread.Sleep(100);
}
You should know where to use threads and they will be beneficial and how many threads you require and what would be the impact of thread on application performance. It depends upon the application that for how much time it would suspend the current thread. I just gave 100 miliseconds, you adjust according to your application.

Related

C# additional threads decreases efficiency of application

I wanted to stress test my new cpu. I made this in about 2 mins.
When I add more threads to it, the efficiency of it decreases dramatically. These are results: (Please note I set the Priority in the taskmanager to High)
1 Thread: After one minute on 1 thread(s), you got to the number/prime 680263811
2 Threads: After one minute on 2 thread(s), you got to the number/prime 360252913
4 Threads: After one minute on 4 thread(s), you got to the number/prime 216150449
There are problems with the code, I just made it as a test. Please don't bash me that it was written horribly... I've kinda had a bad day
static void Main(string[] args)
{
Console.Write("Stress test, how many OS threads?: ");
int thr = int.Parse(Console.ReadLine());
Thread[] t = new Thread[thr];
Stopwatch s = new Stopwatch();
s.Start();
UInt64 it = 0;
UInt64 prime = 0;
for (int i = 0; i < thr; i++)
{
t[i] = new Thread(delegate()
{
while (s.Elapsed.TotalMinutes < 1)
{
it++;
if (it % 2 != 0)// im 100% sure that x % 2 does not give primes, but it uses up the cpu, so idc
{
prime = it;
}
}
Console.WriteLine("After one minute on " + t.Length + " thread(s), you got to the number/prime " + prime.ToString());//idc if this prints 100 times, this is just a test
});
t[i].Start();
}
Console.ReadLine();
}
Question: Can someone explain these unexpected results?
Your threads are incrementing it without any synchronization, so you're going to get weird results like this. Worse, you're also assigning prime without any synchronization.
thread 1: reads 0 from it, then gets unscheduled by the OS for whatever reason
thread 2: reads 0 from it, then increments to 1
thread 2: does work, assigns 1 to prime
... thread 2 repeats for awhile. thread 2 is now up to 7, and is about to check if (it % 2 != 0)
thread 1: regains the CPU
thread 1: increments it to 1
thread 2: assigns 1 to prime --- wat?
The possibilities get even worse as you get to the point where a bit in the high half of it is changing because 64-bit reads and writes are not atomic either although these numbers are a little larger than in the question, after running for longer, wildly variable would be possible ... consider
After some time it = 0x00000000_FFFFFFFF
thread 1 reads reads it (both words)
thread 2 reads the higher word 0x0000000_????????
thread 1 calculates it + 1 0x00000001_00000000
thread 1 writes it (both halves)
thread 2 reads the lower word (and puts this with the already read half) 0x00000000_00000000
While thread 1 was incrementing to 4294967296, thread 2 managed to read 0.
You should apply keyword volatile to variable it and prime.

What is the more efficient syntax to produce an even number in my console project?

I recently started learning c# and learned that there are 2 ways to have my code output even numbers when I write this For loop. I learned the syntax of version 2, which is intuitive to me. However, version 1 was an exampled I found elsewhere online. I want to understand if there is a difference between the two versions.
//Version 1
for (int num = 0; num < 100; num+=2)
{
Console.WriteLine (num);
}
//Version 2
for (int num = 0; num < 100; num++)
{
if (num % 2 == 0)
{
Console.WriteLine (num);
}
}
Of the two possible ways, is there any difference between the two syntaxes? If yes, which is more efficient and why?
For a given value of N (in your sample code, N=100)
Version #1 does N/2 additions
Version #2 does does N additions, plus N integer divisions (a relatively expensive operation).
And you forgot Version 3, bit-twiddling. Bitwise operations are a whole lot cheaper than division, and since we know that integers in the C# world are two's-complement binary values, the state of the low-order bit tells us whether an integer is even or not, thus:
bool isEven( int n ) { return 0 == ( n & 1 ) ; }
We can write a test harness, like this:
class Program
{
public static int Version1(int n)
{
int cnt = 0;
for ( int i = 0 ; i < n ; i+=2 )
{
++cnt;
}
return cnt;
}
public static int Version2(int n)
{
int cnt = 0;
for ( int i = 0 ; i < n ; ++i )
{
if ( i % 2 == 0 )
{
++cnt;
}
}
return cnt;
}
public static int Version3(int n)
{
int cnt = 0;
for ( int i = 0 ; i < n ; ++i )
{
if ( 0 == (i & 1) )
{
++cnt;
}
}
return cnt;
}
private static void Main(string[] args)
{
int n = 500000000;
Stopwatch timer = new Stopwatch();
timer.Start();
Version1( n );
timer.Stop();
Console.WriteLine( "{0:c} -- Version #1 (incrementing by 2)" , timer.Elapsed ) ;
timer.Restart();
Version2(n);
timer.Stop();
Console.WriteLine( "{0:c} -- Version #2 (incrementing by 1 with modulo test)" , timer.Elapsed ) ;
timer.Restart();
Version3(n);
timer.Stop();
Console.WriteLine( "{0:c} -- Version #3 (incrementing by 1 with bit twiddling)" , timer.Elapsed ) ;
return;
}
}
And find out which is actually faster. The above program runs the loop 500,000,000 times so we get numbers that are big enough to measure.
Here are the timings I get with VS 2013:
Debug build (non-optimized):
00:00:00.5500486 -- Version #1 (incrementing by 2)
00:00:02.0843776 -- Version #2 (incrementing by 1 with modulo test)
00:00:01.2507272 -- Version #3 (incrementing by 1 with bit twiddling)
Version #2 is 3.789 times slower than version #1
Version #3 is 2.274 times slower than version #1
Release build (optimized)
00:00:00.1680107 -- Version #1 (incrementing by 2)
00:00:00.5109271 -- Version #2 (incrementing by 1 with modulo test)
00:00:00.3367961 -- Version #3 (incrementing by 1 with bit twiddling)
Version #2 is 3.041 times slower than version #1
Version #3 is 2.005 times slower than version #1
You can exactly measure which method is faster, on a precision of 10000th of a millisecond, also known as "Tick" in the .Net framework.
This is done by using the Stopwatch class in the System.Diagnostic namespace:
var sw1 = new System.Diagnostics.Stopwatch();
var sw2 = new System.Diagnostics.Stopwatch();
//Version 1
sw1.Start();
for (int num = 0; num < 100; num += 2)
{
Console.WriteLine(num);
}
sw1.Stop();
//Version 2
sw2.Start();
for (int num = 0; num < 100; num++)
{
if (num % 2 == 0)
{
Console.WriteLine(num);
}
}
sw2.Stop();
Console.Clear();
Console.WriteLine("Ticks for first method: " + sw1.ElapsedTicks);
Console.WriteLine("Ticks for second method: " + sw2.ElapsedTicks);
The output will show that the first method is faster.
Why is this the case?
In the first version, ignoring the console output, there is only one operation performed (+= 2) and in the end, the program goes through 50 cycles of the loop.
In the second version, it needs to do two operations (++ and % 2) and one more comparison (num % 2 == 0) in addition to 100 cycles in the loop.
If you measure the program exactly like this, then it will have a large difference in time, like multiple milliseconds. This is because the Console.WriteLine is actually taking up a lot of time. Since it's done 50 times more in the second version, it takes much more time. If you want to measure the algorithm alone, omit the console output.
If you do that, the difference in ticks on my machine is 24 ticks to 43 ticks, on average.
So in conclusion, the first method is more efficient, by about 19/10000 milliseconds.
Version 1 is "faster" from a pure analysis viewpoint (independent of compilation optimizations). But as dbarnes mentioned in the comments to your question, what you code in C# and what comes out the other side of the compiler may be completely different. Version 2 is "slower" because it has more loop iterations to cycle through, and it has slightly larger code complexity with that if-statement.
Don't concern yourself too much with optimization at first (it is called "premature optimization" to be concerned with performance before you know it exists and can prove that it is a problem worth solving). Simply code to learn, then refactor, then look into performance issues if any exist.
In fact, I'd speculate that the compiler could be configured to "unroll" both of those loops: it gets rid of the looping and simply duplicates the loop body 50 or 100 times for version 1 or 2 respectively.
Actually the adding mechanism ( Version 1 ) is a little better
The result of the addition was better than the modulus(the %) by 0.0070000 milliseconds over the course of 2 million or 200,000 iterations
But seriously… unless you have a mission critical micro-optimization need, stick with the modulus operator for easy to read code rather than trying to save roughly 00.0070000 milliseconds execution time.
Here Is The Source
Version 1 is more efficient in the sense that it iterates half as many times with the same output. Version 1 will iterate 50 times where as version 2 will iterate 100 times but only print half the results. However, I think the second shows your intentions better.
Version 1 is more efficient. Because "version 2" takes one more comparison than "version 1", while "version 1" takes two comparisons.
//Version 1
for (int num = 0; num < 100; num+=2) // Two comparisons
{
Console.WriteLine (num);
}
//Version 2
for (int num = 0; num < 100; num++) // Two comparisons
{
if (num % 2 == 0) // One comparison
{
Console.WriteLine (num);
}
}
This way is faster than method 1 and method 2 because recursion is faster than looping.
var sw3 = new System.Diagnostics.Stopwatch();
Action<int, int> printEvens = null;
printEvens = (index, count) =>
{
if (index % 2 == 0)
Console.WriteLine(index.ToString());
if (index < count)
printEvens(++index, count);
};
sw3.Start();
printEvens(0, 100);
sw3.Stop();
The output on my machine is..
Method 1: 96282
Method 2: 184336
Method 3: Recursion 59188

No performance gains with Parallel.ForEach and Regex?

I have a program which color codes a returned results set a certain way depending on what the results are. Due to the length of time it takes to color-code the results (currently being done with Regex and RichTextBox.Select + .SelectionColor), I cut off color-coding at 400 results. At around that number it takes about 20 seconds, which is just about max time of what I'd consider reasonable.
To try an improve performance I re-wrote the Regex part to use a Parallel.ForEach loop to iterate through the MatchCollection, but the time was about the same (18-19 seconds vs 20)! Is just not a job that lends itself to Parallel programming very well? Should I try something different? Any advice is welcome. Thanks!
PS: Thought it was a bit strange that my CPU utilization never went about 14%, with or without Parallel.ForEach.
Code
MatchCollection startMatches = Regex.Matches(tempRTB.Text, startPattern);
object locker = new object();
System.Threading.Tasks.Parallel.ForEach(startMatches.Cast<Match>(), m =>
{
int i = 0;
foreach (Group g in m.Groups)
{
if (i > 0 && i < 5 && g.Length > 0)
{
tempRTB.Invoke(new Func<bool>(
delegate
{
lock (locker)
{
tempRTB.Select(g.Index, g.Length);
if ((i & 1) == 0) // Even number
tempRTB.SelectionColor = Namespace.Properties.Settings.Default.ValueColor;
else // Odd number
tempRTB.SelectionColor = Namespace.Properties.Settings.Default.AttributeColor;
return true;
}
}));
}
else if (i == 5 && g.Length > 0)
{
var result = tempRTB.Invoke(new Func<string>(
delegate
{
lock (locker)
{
return tempRTB.Text.Substring(g.Index, g.Length);
}
}));
MatchCollection subMatches = Regex.Matches((string)result, pattern);
foreach (Match subMatch in subMatches)
{
int j = 0;
foreach (Group subGroup in subMatch.Groups)
{
if (j > 0 && subGroup.Length > 0)
{
tempRTB.Invoke(new Func<bool>(
delegate
{
lock (locker)
{
tempRTB.Select(g.Index + subGroup.Index, subGroup.Length);
if ((j & 1) == 0) // Even number
tempRTB.SelectionColor = Namespace.Properties.Settings.Default.ValueColor;
else // Odd number
tempRTB.SelectionColor = Namespace.Properties.Settings.Default.AttributeColor;
return true;
}
}));
}
j++;
}
}
}
i++;
}
});
Virtually no aspect of your program is actually able to run in parallel.
The generation of the matches needs to be done sequentially. It can't find the second match until it has already found the first. Parallel.ForEach will, at best, allow you to process the results of the sequence in parallel, but they are still generated sequentially. This is where the majority of your time consuming work seems to be, and there are no gains there.
On top of that, you aren't really processing the results in parallel either. The majority of code run in the body of your loop is all inside an invoke to the UI thread, which means it's all being run by a single thread.
In short, only a tiny, tiny bit of your program is actually run in parallel, and using parallelization in general adds some overhead; it sounds like you're just barely getting more than that overhead. There isn't really much that you did wrong, the operation just inherently doesn't lend itself to parallelization, unless there is an effective way of breaking up the initial string into several smaller chucks that the regex can parse individually (in parallel).
The most time in your code is most likely spent in the part that actually selects the text in the richtext box and sets the color.
This code is impossible to execute in parallel, because it has to be marshalled to the UI thread - which you do via tempRTB.Invoke.
Furthermore, you explicitly make sure that the highlighting is not executed in parallel but sequentially by using the lock statement. This is unnecessary, because all of that code is run on the single UI thread anyway.
You could try to improve your performance by suspending the layouting of your UI while you select and color the text in the RTB:
tempRTB.SuspendLayout();
// your loop
tempRTB.ResumeLayout();

How come this algorithm in Ruby runs faster than in Parallel'd C#?

The following ruby code runs in ~15s. It barely uses any CPU/Memory (about 25% of one CPU):
def collatz(num)
num.even? ? num/2 : 3*num + 1
end
start_time = Time.now
max_chain_count = 0
max_starter_num = 0
(1..1000000).each do |i|
count = 0
current = i
current = collatz(current) and count += 1 until (current == 1)
max_chain_count = count and max_starter_num = i if (count > max_chain_count)
end
puts "Max starter num: #{max_starter_num} -> chain of #{max_chain_count} elements. Found in: #{Time.now - start_time}s"
And the following TPL C# puts all my 4 cores to 100% usage and is orders of magnitude slower than the ruby version:
static void Euler14Test()
{
Stopwatch sw = new Stopwatch();
sw.Start();
int max_chain_count = 0;
int max_starter_num = 0;
object locker = new object();
Parallel.For(1, 1000000, i =>
{
int count = 0;
int current = i;
while (current != 1)
{
current = collatz(current);
count++;
}
if (count > max_chain_count)
{
lock (locker)
{
max_chain_count = count;
max_starter_num = i;
}
}
if (i % 1000 == 0)
Console.WriteLine(i);
});
sw.Stop();
Console.WriteLine("Max starter i: {0} -> chain of {1} elements. Found in: {2}s", max_starter_num, max_chain_count, sw.Elapsed.ToString());
}
static int collatz(int num)
{
return num % 2 == 0 ? num / 2 : 3 * num + 1;
}
How come ruby runs faster than C#? I've been told that Ruby is slow. Is that not true when it comes to algorithms?
Perf AFTER correction:
Ruby (Non parallel): 14.62s
C# (Non parallel): 2.22s
C# (With TPL): 0.64s
Actually, the bug is quite subtle, and has nothing to do with threading. The reason that your C# version takes so long is that the intermediate values computed by the collatz method eventually start to overflow the int type, resulting in negative numbers which may then take ages to converge.
This first happens when i is 134,379, for which the 129th term (assuming one-based counting) is 2,482,111,348. This exceeds the maximum value of 2,147,483,647 and therefore gets stored as -1,812,855,948.
To get good performance (and correct results) on the C# version, just change:
int current = i;
…to:
long current = i;
…and:
static int collatz(int num)
…to:
static long collatz(long num)
That will bring down your performance to a respectable 1.5 seconds.
Edit: CodesInChaos raises a very valid point about enabling overflow checking when debugging math-oriented applications. Doing so would have allowed the bug to be immediately identified, since the runtime would throw an OverflowException.
Should be:
Parallel.For(1L, 1000000L, i =>
{
Otherwise, you have integer overfill and start checking negative values. The same collatz method should operate with long values.
I experienced something like that. And I figured out that's because each of your loop iterations need to start other thread and this takes some time, and in this case it's comparable (I think it's more time) than the operations you acctualy do in the loop body.
There is an alternative for that: You can get how many CPU cores you have and than use a parallelism loop with the same number of iterations you have cores, each loop will evaluate part of the acctual loop you want, it's done by making an inner for loop that depends on the parallel loop.
EDIT: EXAMPLE
int start = 1, end = 1000000;
Parallel.For(0, N_CORES, n =>
{
int s = start + (end - start) * n / N_CORES;
int e = n == N_CORES - 1 ? end : start + (end - start) * (n + 1) / N_CORES;
for (int i = s; i < e; i++)
{
// Your code
}
});
You should try this code, I'm pretty sure this will do the job faster.
EDIT: ELUCIDATION
Well, quite a long time since I answered this question, but I faced the problem again and finally understood what's going on.
I've been using AForge implementation of Parallel for loop, and it seems like, it fires a thread for each iteration of the loop, so, that's why if the loop takes relatively a small amount of time to execute, you end up with a inefficient parallelism.
So, as some of you pointed out, System.Threading.Tasks.Parallel methods are based on Tasks, which are kind of a higher level of abstraction of a Thread:
"Behind the scenes, tasks are queued to the ThreadPool, which has been enhanced with algorithms that determine and adjust to the number of threads and that provide load balancing to maximize throughput. This makes tasks relatively lightweight, and you can create many of them to enable fine-grained parallelism."
So yeah, if you use the default library's implementation, you won't need to use this kind of "bogus".

Trying to understand multi-threading in C#

I'm trying to understand the basics of multi-threading so I built a little program that raised a few question and I'll be thankful for any help :)
Here is the little program:
class Program
{
public static int count;
public static int max;
static void Main(string[] args)
{
int t = 0;
DateTime Result;
Console.WriteLine("Enter Max Number : ");
max = int.Parse(Console.ReadLine());
Console.WriteLine("Enter Thread Number : ");
t = int.Parse(Console.ReadLine());
count = 0;
Result = DateTime.Now;
List<Thread> MyThreads = new List<Thread>();
for (int i = 1; i < 31; i++)
{
Thread Temp = new Thread(print);
Temp.Name = i.ToString();
MyThreads.Add(Temp);
}
foreach (Thread th in MyThreads)
th.Start();
while (count < max)
{
}
Console.WriteLine("Finish , Took : " + (DateTime.Now - Result).ToString() + " With : " + t + " Threads.");
Console.ReadLine();
}
public static void print()
{
while (count < max)
{
Console.WriteLine(Thread.CurrentThread.Name + " - " + count.ToString());
count++;
}
}
}
I checked this with some test runs:
I made the maximum number 100, and it seems to be that the fastest execution time is with 2 threads which is 80% faster than the time with 10 threads.
Questions:
1) Threads 4-10 don't print even one time, how can it be?
2) Shouldn't more threads be faster?
I made the maximum number 10000 and disabled printing.
With this configuration, 5 threads seems to be fastest.
Why there is a change compared to the first check?
And also in this configuration (with printing) all the threads print a few times. Why is that different from the first run where only a few threads printed?
Is there is a way to make all the threads print one by one? In a line or something like that?
Thank you very much for your help :)
Your code is certainly a first step into the world of threading, and you've just experienced the first (of many) headaches!
To start with, static may enable you to share a variable among the threads, but it does not do so in a thread safe manner. This means your count < max expression and count++ are not guaranteed to be up to date or an effective guard between threads. Look at the output of your program when max is only 10 (t set to 4, on my 8 processor workstation):
T0 - 0
T0 - 1
T0 - 2
T0 - 3
T1 - 0 // wait T1 got count = 0 too!
T2 - 1 // and T2 got count = 1 too!
T2 - 6
T2 - 7
T2 - 8
T2 - 9
T0 - 4
T3 - 1 // and T3 got count = 1 too!
T1 - 5
To your question about each thread printing one-by-one, I assume you're trying to coordinate access to count. You can accomplish this with synchronization primitives (such as the lock statement in C#). Here is a naive modification to your code which will ensure only max increments occur:
static object countLock = new object();
public static void printWithLock()
{
// loop forever
while(true)
{
// protect access to count using a static object
// now only 1 thread can use 'count' at a time
lock (countLock)
{
if (count >= max) return;
Console.WriteLine(Thread.CurrentThread.Name + " - " + count.ToString());
count++;
}
}
}
This simple modification makes your program logically correct, but also slow. The sample now exhibits a new problem: lock contention. Every thread is now vying for access to countLock. We've made our program thread safe, but without any benefits of parallelism!
Threading and parallelism is not particularly easy to get right, but thankfully recent versions of .Net come with the Task Parallel Library (TPL) and Parallel LINQ (PLINQ).
The beauty of the library is how easy it would be to convert your current code:
var sw = new Stopwatch();
sw.Start();
Enumerable.Range(0, max)
.AsParallel()
.ForAll(number =>
Console.WriteLine("T{0}: {1}",
Thread.CurrentThread.ManagedThreadId,
number));
Console.WriteLine("{0} ms elapsed", sw.ElapsedMilliseconds);
// Sample output from max = 10
//
// T9: 3
// T9: 4
// T9: 5
// T9: 6
// T9: 7
// T9: 8
// T9: 9
// T8: 1
// T7: 2
// T1: 0
// 30 ms elapsed
The output above is an interesting illustration of why threading produces "unexpected results" for newer users. When threads execute in parallel, they may complete chunks of code at different points in time or one thread may be faster than another. You never really know with threading!
Your print function is far from thread safe, that's why 4-10 doesn't print. All threads share the same max and count variables.
Reason for the why more threads slows you down is likely the state change taking place each time the processor changes focus between each thread.
Also, when you're creating a lot of threads, the system needs to allocate new ones. Most of the time it is now advisable to use Tasks instead, as they are pulled from a system managed thread-pool. And thus doesn't necessarily have to be allocated. The creation of a distinct new thread is rather expensive.
Take a look here anyhow: http://msdn.microsoft.com/en-us/library/aa645740(VS.71).aspx
Look carefuly:
t = int.Parse(Console.ReadLine());
count = 0;
Result = DateTime.Now;
List<Thread> MyThreads = new List<Thread>();
for (int i = 1; i < 31; i++)
{
Thread Temp = new Thread(print);
Temp.Name = i.ToString();
MyThreads.Add(Temp);
}
I think you missed a variable t ( i < 31).
You should read many books on parallel and multithreaded programming before writing code, because programming language is just a tool. Good luck!

Categories