Related
This is an example about Thread Local Storage (TLS) from Apress parallel programming book. I know that if we have 4 cores computer 4 thread can run parallel in same time. In this example we create 10 task and we suppose that have 4 cores computer. Each Thread local storage live in on thread so when start 10 task parallel only 4 thread perform. And We have 4 TLS so 10 task try to change 4 Thread local storage object. i want to ask how Tls prevent data race problem when thread count < Task count ??
using System;
using System.Threading;
using System.Threading.Tasks;
namespace Listing_04
{
class BankAccount
{
public int Balance
{
get;
set;
}
}
class Listing_04
{
static void Main(string[] args)
{
// create the bank account instance
BankAccount account = new BankAccount();
// create an array of tasks
Task<int>[] tasks = new Task<int>[10];
// create the thread local storage
ThreadLocal<int> tls = new ThreadLocal<int>();
for (int i = 0; i < 10; i++)
{
// create a new task
tasks[i] = new Task<int>((stateObject) =>
{
// get the state object and use it
// to set the TLS data
tls.Value = (int)stateObject;
// enter a loop for 1000 balance updates
for (int j = 0; j < 1000; j++)
{
// update the TLS balance
tls.Value++;
}
// return the updated balance
return tls.Value;
}, account.Balance);
// start the new task
tasks[i].Start();
}
// get the result from each task and add it to
// the balance
for (int i = 0; i < 10; i++)
{
account.Balance += tasks[i].Result;
}
// write out the counter value
Console.WriteLine("Expected value {0}, Balance: {1}",
10000, account.Balance);
// wait for input before exiting
Console.WriteLine("Press enter to finish");
Console.ReadLine();
}
}
}
We have 4 TLS so 10 task try to change 4 Thread local storage object
In your example, you could have anywhere between 1 and 10 TLS slots. This is because a) you are not managing your threads explicitly and so the tasks are executed using the thread pool, and b) the thread pool creates and destroys threads over time according to demand.
A loop of only 1000 iterations will completely almost instantaneously. So it's likely all ten of your tasks will get through the thread pool before the thread pool decides a work item has been waiting long enough to justify adding any new threads. But there is no guarantee of this.
Some important parts of the documentation include these statements:
By default, the minimum number of threads is set to the number of processors on a system
and
When demand is low, the actual number of thread pool threads can fall below the minimum values.
In other words, on your four-core system, the default minimum number of threads is four, but the actual number of threads active in the thread pool could in fact be less than that. And if the tasks take long enough to execute, the number of active threads could rise above that.
The biggest thing to keep in mind here is that using TLS in the context of a thread pool is almost certainly the wrong thing to do.
You use TLS when you have control over the threads, and you want a thread to be able to maintain some data private or unique to that thread. That's the opposite of what happens when you are using the thread pool. Even in the simplest case, multiple tasks can use the same thread, and so would wind up sharing TLS. And in more complicated scenarios, such as when using await, a single task could wind up executed in different threads, and so that one task could wind up using different TLS values depending on what thread is assigned to that task at that moment.
how Tls prevent data race problem when thread count < Task count ??
That depends on what "data race problem" you're talking about.
The fact is, the code you posted is filled with problems that are at the very least odd, if not outright wrong. For example, you are passing account.Balance as the initial value for each task. But why? This value is evaluated when you create the task, before it could ever be modified later, so what's the point of passing it?
And if you thought you were passing whatever the current value is when the task starts, that seems like that would be wrong too. Why would it be valid to make the starting value for a given task vary according to how many tasks had already completed and been accounted for in your later loop? (To be clear: that's not what's happening…but even if it were, it'd be a strange thing to do.)
Beyond all that, it's not clear what you thought using TLS here would accomplish anyway. When each task starts, you reinitialize the TLS value to 0 (i.e. the value of account.Balance that you've passed to the Task<int> constructor). So no thread involved ever sees a value other than 0 during the context of executing any given task. A local variable would accomplish exactly the same thing, without the overhead of TLS and without confusing anyone who reads the code and tries to figure out why TLS was used when it adds no value to the code.
So, does TLS solve some sort of "data race problem"? Not in this example, it doesn't appear to. So asking how it does that is impossible to answer. It doesn't do that, so there is no "how".
For what it's worth, I modified your example slightly so that it would report the individual threads that were assigned to the tasks. I found that on my machine, the number of threads used varied between two and eight. This is consistent with my eight-core machine, with the variation due to how much the first thread in the pool can get done before the pool has initialized additional threads and assigned tasks to them. Most commonly, I would see the first thread completing between three and five of the tasks, with the remaining tasks handled by remaining individual threads.
In each case, the thread pool created eight threads as soon as the tasks were started. But most of the time, at least one of those threads wound up unused, because the other threads were able to complete the tasks before the pool was saturated. That is, there is overhead in the thread pool just managing the tasks, and in your example the tasks are so inexpensive that this overhead allows one or more thread pool threads to finish one task before the thread pool needs that thread for another.
I've copied that version below. Note that I also added a delay between trial iterations, to allow the thread pool to terminate the threads it created (on my machine, this took 20 seconds, hence the delay time hard-coded…you can see the threads being terminated in the debugger output).
static void Main(string[] args)
{
while (_PromptContinue())
{
// create the bank account instance
BankAccount account = new BankAccount();
// create an array of tasks
Task<int>[] tasks = new Task<int>[10];
// create the thread local storage
ThreadLocal<int> tlsBalance = new ThreadLocal<int>();
ThreadLocal<(int Id, int Count)> tlsIds = new ThreadLocal<(int, int)>(
() => (Thread.CurrentThread.ManagedThreadId, 0), true);
for (int i = 0; i < 10; i++)
{
int k = i;
// create a new task
tasks[i] = new Task<int>((stateObject) =>
{
// get the state object and use it
// to set the TLS data
tlsBalance.Value = (int)stateObject;
(int id, int count) = tlsIds.Value;
tlsIds.Value = (id, count + 1);
Console.WriteLine($"task {k}: thread {id}, initial value {tlsBalance.Value}");
// enter a loop for 1000 balance updates
for (int j = 0; j < 1000; j++)
{
// update the TLS balance
tlsBalance.Value++;
}
// return the updated balance
return tlsBalance.Value;
}, account.Balance);
// start the new task
tasks[i].Start();
}
// Make sure this thread isn't busy at all while the thread pool threads are working
Task.WaitAll(tasks);
// get the result from each task and add it to
// the balance
for (int i = 0; i < 10; i++)
{
account.Balance += tasks[i].Result;
}
// write out the counter value
Console.WriteLine("Expected value {0}, Balance: {1}", 10000, account.Balance);
Console.WriteLine("{0} thread ids used: {1}",
tlsIds.Values.Count,
string.Join(", ", tlsIds.Values.Select(t => $"{t.Id} ({t.Count})")));
System.Diagnostics.Debug.WriteLine("done!");
_Countdown(TimeSpan.FromSeconds(20));
}
}
private static void _Countdown(TimeSpan delay)
{
System.Diagnostics.Stopwatch sw = System.Diagnostics.Stopwatch.StartNew();
TimeSpan remaining = delay - sw.Elapsed,
sleepMax = TimeSpan.FromMilliseconds(250);
int cchMax = $"{delay.TotalSeconds,2:0}".Length;
string format = $"\r{{0,{cchMax}:0}}", previousText = null;
while (remaining > TimeSpan.Zero)
{
string nextText = string.Format(format, remaining.TotalSeconds);
if (previousText != nextText)
{
Console.Write(format, remaining.TotalSeconds);
previousText = nextText;
}
Thread.Sleep(remaining > sleepMax ? sleepMax : remaining);
remaining = delay - sw.Elapsed;
}
Console.Write(new string(' ', cchMax));
Console.Write('\r');
}
private static bool _PromptContinue()
{
Console.Write("Press Esc to exit, any other key to proceed: ");
try
{
return Console.ReadKey(true).Key != ConsoleKey.Escape;
}
finally
{
Console.WriteLine();
}
}
Say I have a class A and a class B representing tasks.
I want to perform an experiment, and for the experiment to start I need to finish at least 5 B tasks and only 1 A task.
I have the following classes
abstract class Task
{
public int Id;
public void Start(object resetEvent)
{
EventWaitHandle ewh = (EventWaitHandle)resetEvent;
Thread.Sleep(new Random(DateTime.Now.Ticks.GetHashCode()).Next(5000, 14000));
Console.WriteLine("{0} {1} starts",this.GetType().Name, Id);
ewh.Set();
}
}
class A : Task
{
static int ID = 1;
public A(EventWaitHandle resetEvent)
{
Id = ID++;
new Thread(StartTask).Start(resetEvent);
}
}
class B : Task
{
static int ID = 1;
public B(EventWaitHandle resetEvent)
{
Id = ID++;
new Thread(StartTask).Start(resetEvent);
}
}
and the following main
static void Main()
{
A a;
B[] bs = new B[20];
int numberOfBs = 0;
EventWaitHandle aResetEvent = new AutoResetEvent(false);
EventWaitHandle bResetEvent = new AutoResetEvent(false);
a = new A(aResetEvent);
for (int i = 0; i < bs.Length; i++)
bs[i] = new B(bResetEvent);
while (numberOfBs < 5)
{
bResetEvent.WaitOne();
numberOfBs++;
}
aResetEvent.WaitOne();
Console.WriteLine("Experiment started with {0} B's!", numberOfBs);
Thread.Sleep(3000); // check how many B's got in the middle
Console.WriteLine("Experiment ended with {0} B's!", numberOfBs);
}
now I have few problems/questions:
How can I wait for only N signals out of possible M?
Can I achieve the result I'm looking for with only 1 AutoResetEvent?
I don't understand why all the tasks are printed together, I expected each task to be printed when it is done and now when everything is done.
is the following code thread safe?
.
while (numberOfBs < 5)
{
bResetEvent.WaitOne();
numberOfBs++;
}
could it be that couple of threads signal together? if so, can I fix that using lock on bResetEvent?
1.How can I wait for only N signals out of possible M?
Just as you do here (sort of…see answer to #4).
2.Can I achieve the result I'm looking for with only 1 AutoResetEvent?
Yes. But you will need two counters in that case (one for the A type and one for the B type), and they will need to be accessed in a thread-safe way, e.g. with the Interlocked class, or using a lock statement. All threads, A and B types, will share the same AutoResetEvent, but increment their own type's counter. The main thread can monitor each counter and process once both counters are at their desired value (1 for the A counter, 5 for the B counter).
I'd recommend using the lock statement approach, as it's simpler and would allow you to avoid using AutoResetEvent altogether (the lock statement uses the Monitor class, which provides some functionality similar to AutoResetEvent, while also providing the synchronization needed to ensure coherent use of the counters.
Except that you've written in the comments you have to use AutoResetEvent (why?), so I guess you're stuck with Interlocked (no point in using lock if you're not going to take full advantage).
3.I don't understand why all the tasks are printed together, I expected each task to be printed when it is done and now when everything is done.
Because you have a bug. You should be creating a single Random instance and using it to determine the duration of every task. You can either compute the durations in the thread that creates each task, or you can synchronize access (e.g. with lock) and use the same Random object in multiple threads.
What you can't do is create a whole new Random object using the same seed value for every thread, because then each thread (or at least large blocks of them, depending on timing) is going to wind up getting the exact same "random" number to use as its duration.
You see all the output coming out together, because that's when it happens: all together.
(And yes, if you create multiple Random objects in quick succession, they will all get the same seed, whether you use DateTime.Now yourself explicitly, or just let the Random class do it. The tick counter used for the seed is not updated frequently enough for concurrently running threads to see different values.)
4.is the following code thread safe?
The code in question:
while (numberOfBs < 5)
{
bResetEvent.WaitOne();
numberOfBs++;
}
…is thread safe, because the only data shared between the thread executing that loop and any other thread is the AutoResetEvent object, and that object is itself thread-safe.
That is, for the usual understanding of "thread safe". I highly recommend you read Eric Lippert's article What is this thing you call "thread safe"? Asking if something is thread-safe is a much more complicated question that you probably realize.
In particular, while the code is thread-safe in the usual way (i.e. data remains coherent), as you note it is possible for more than one thread to reach the Set() call before the main thread can react to the first. Thus you may miss some notifications.
The task that requires taks A and B reach certain changes could be notified each time a task is done. When it gets notified it could check if the conditions are good and proceed only then.
Output:
Task 3 still waiting: A0, B0
B reached 1
Task 3 still waiting: A0, B1
A reached 1
Task 3 still waiting: A1, B1
B reached 2
Task 3 still waiting: A1, B2
B reached 3
Task 3 still waiting: A1, B3
A reached 2
Task 3 still waiting: A2, B3
B reached 4
Task 3 still waiting: A2, B4
B reached 5
Task 3 done: A2, B5
A reached 3
B reached 6
B reached 7
B reached 8
B reached 9
B reached 10
All done
Program:
class Program
{
static int stageOfA = 0;
static int stageOfB = 0;
private static readonly AutoResetEvent _signalStageCompleted = new AutoResetEvent(false);
static void DoA()
{
for (int i = 0; i < 3; i++) {
Thread.Sleep(100);
Interlocked.Increment(ref stageOfA);
Console.WriteLine($"A reached {stageOfA}");
_signalStageCompleted.Set();
}
}
static void DoB()
{
for (int i = 0; i < 10; i++)
{
Thread.Sleep(50);
Interlocked.Increment(ref stageOfB);
Console.WriteLine($"B reached {stageOfB}");
_signalStageCompleted.Set();
}
}
static void DoAfterB5andA1()
{
while( (stageOfA < 1) || (stageOfB < 5))
{
Console.WriteLine($"Task 3 still waiting: A{stageOfA}, B{stageOfB}");
_signalStageCompleted.WaitOne();
}
Console.WriteLine($"Task 3 done: A{stageOfA}, B{stageOfB}");
}
static void Main(string[] args)
{
Task[] taskArray = { Task.Factory.StartNew(() => DoA()),
Task.Factory.StartNew(() => DoB()),
Task.Factory.StartNew(() => DoAfterB5andA1()) };
Task.WaitAll(taskArray);
Console.WriteLine("All done");
Console.ReadLine();
}
}
I have a program I am writing that will run a variety of tasks. I have set up what I have called a "Task Queue" in which I will continually grab the next task to process (if there is one) and start a new thread to handle that task. However, I want to limit the amount of threads that can spawn at one time for apparent reasons. I created a variable to keep up with the max threads to spawn and one for the current thread count. I was thinking of using a lock to try and accurately keep up with the current thread count. Here is my general idea.
public class Program {
private static int mintThreadCount;
private static int mintMaxThreadCount = 10;
private static object mobjLock = new object();
static void Main(string[] args) {
mintThreadCount = 0;
int i = 100;
while(i > 0) {
StartNewThread();
i--;
}
Console.Read();
}
private static void StartNewThread() {
lock(mobjLock) {
if(mintThreadCount < mintMaxThreadCount) {
Thread newThread = new Thread(StartTask);
newThread.Start(mintThreadCount);
mintThreadCount++;
}
else {
Console.WriteLine("Max Thread Count Reached.");
}
}
}
private static void StartTask(object iCurrentThreadCount) {
int id = new Random().Next(0, 1000000);
Console.WriteLine("New Thread with id of: " + id.ToString() + " Started. Current Thread count: " + ((int)iCurrentThreadCount).ToString());
Thread.Sleep(new Random().Next(0, 3000));
lock(mobjLock) {
Console.WriteLine("Ending thread with id of: " + id.ToString() + " now.");
mintThreadCount--;
Console.WriteLine("Thread space release by id of: " + id.ToString() + " . Thread count now at: " + mintThreadCount);
}
}
}
Since I am locking in two places to access the same variable (increment when starting the new thread and decrement when ending it) is there a chance that the thread waiting on the lock to decrement could get hung up and never end? Thereby reaching max thread count and never being able to start another one? Any alternate suggestions to my method?
Easiest question first… :)
…is there a chance that the thread waiting on the lock to decrement could get hung up and never end?
No, not in the code you posted. None of the code holds a lock while waiting for the count to change, or anything like that. You only ever take the lock, then either modify the count or emit a message, and immediately release the lock. So no thread will hold the lock indefinitely, nor are there nested locks (which could lead to deadlock if done incorrectly).
Now, that said: from the code you posted and your question, it's not entirely clear what the intent here is. The code as written will indeed limit the number of threads created. But once that limit is reached (and it will do so quickly), the main loop will just spin, reporting "Max Thread Count Reached.".
Indeed, with a total loop count of 100, I think it's possible that the entire loop could finish before the first thread even gets to run, depending on what else is tying up CPU cores on your system. If some threads do get to run and it happens that some of them get very low durations to sleep, there's a chance that you might sneak in a few more threads later. But most of the iterations of the loop will see the thread count at the maximum, report the limit has been reached and continue with the next iteration of the loop.
You write in the comments (something you should really put in the question itself, if you think it's relevant) that "the main thread should never be blocked". Of course, the question there is, what is the main thread doing when not blocked? How will the main thread know if and when to try to schedule a new thread?
These are important details, if you want a really useful answer.
Note that you've been offered the suggestion of using a semaphore (specifically, SemaphoreSlim). This could be a good idea, but note that that class is typically used to coordinate multiple threads all competing for the same resource. For it to be useful, you'd actually have more than 10 threads, with the semaphore ensuring that only 10 get to run at a given time.
In your case, it seems to me that you are actually asking how to avoid creating the extra thread in the first place. I.e. you want the main loop to check the count and just not create a thread at all if the maximum count is reached. In that case, one possible solution might be to just use the Monitor class directly:
private static void StartNewThread() {
lock(mobjLock) {
while (mintThreadCount >= mintMaxThreadCount) {
Console.WriteLine("Max Thread Count Reached.");
Monitor.Wait(mobjLock);
}
Thread newThread = new Thread(StartTask);
newThread.Start(mintThreadCount);
mintThreadCount++;
}
}
}
The above will cause the StartNewThread() method to wait until the count is below the maximum, and then will always create a new thread.
Of course, each thread needs to signal that it's updated the count, so that the above loop can be released from the wait and check the count:
private readonly Random _rnd = new Random();
private static void StartTask(object iCurrentThreadCount) {
int id = _rnd.Next(0, 1000000);
Console.WriteLine("New Thread with id of: " + id.ToString() + " Started. Current Thread count: " + ((int)iCurrentThreadCount).ToString());
Thread.Sleep(_rnd.Next(0, 3000));
lock(mobjLock) {
Console.WriteLine("Ending thread with id of: " + id.ToString() + " now.");
mintThreadCount--;
Console.WriteLine("Thread space release by id of: " + id.ToString() + " . Thread count now at: " + mintThreadCount);
Monitor.Pulse(mobjLock);
}
}
The problem with the above is that it will block the main loop. Which if I understood correctly, you don't want.
(Note: you have a common-but-serious bug in your code, in that you create a new Random object each time you want a random number. To use the Random class correctly, you must create just one instance and reuse it as you want new random numbers. I've adjusted the code example above to fix that problem).
One of the other problems, both with the above, and with your original version, is that each new task is assigned a brand new thread. Threads are expensive to create and even to simply exist, which is why thread pools exist. Depending on what your actual scenario is, it's possible that you should just be using e.g. the Parallel, ParallelEnumerable, or Task to manage your tasks.
But if you really want to do this all explicitly, one option is to simply start up ten threads, and have them retrieve data to operate on from a BlockingCollection<T>. Since you start exactly ten threads, you know you'll never have more than that running. When there is enough work for all ten threads to be busy, they will be. Otherwise, the queue will be empty and some or all will be waiting for new data to show in the queue. Idle, but not using any CPU resources.
For example:
private BlockingCollection<int> _queue = new BlockingCollection<int>();
private static void StartThreads() {
for (int i = 0; i < mintMaxThreadCount; i++) {
new Thread(StartTask).Start();
}
}
private static void StartTask() {
// NOTE: a random number can't be a reliable "identification", as two or
// more threads could theoretically get the same "id".
int id = new Random().Next(0, 1000000);
Console.WriteLine("New Thread with id of: " + id.ToString() + " Started.");
foreach (int i in _queue) {
Thread.Sleep(i);
}
Thread.Sleep(new Random().Next(0, 3000));
}
You'd call StartThreads() just once somewhere, rather than calling your other StartNewThread() method multiple times. Presumably, before the while (true) loop you mentioned.
Then as the need to process some task, you just add data to the queue, e.g.:
_queue.Add(_rnd.Next(0, 3000));
When you want the threads to all exit (e.g. after your main loop exits, however that happens):
_queue.CompleteAdding();
That will cause each of the foreach loops in progress to end, letting each thread exit.
Of course, the T type parameter for BlockingCollection<T> can be anything. Presumably, it will be whatever in your case actually represents a "task". I used int, only because that was effectively your "task" in your example (i.e. the number of milliseconds the thread should sleep).
Then your main thread can just do whatever it normally does, calling the Add() method to dispatch new work to your consumer threads as needed.
Again, without more details I can't really comment on whether this approach would be better than using one of the built-in task-running mechanisms in .NET. But it should work well, given what you've explained so far.
I have a very simple program counting the characters in a string. An integer threadnum sets the number of threads and divides the data by threadnum accordingly into chunks for each thread to process.
Each thread increments the values contained in a shared dictionary, building a character historgram.
private Dictionary<UInt32, int> dict = new Dictionary<UInt32, int>();
In order to wait for all threads to finish and continue with the main process, I invoke Thread.Join
Initially I had a local dictionary for each thread which get merged afterwards, but a shared dictionary worked fine, without locking.
No references are locked in the method BuildDictionary, though locking the dictionary did not significantly impact thread-execution time.
Each thread is timed, and the resulting dictionary compared.
The dictionary content is the same regardless of a single or multiple threads - as it should be.
Each thread takes a fraction determined by threadnum to complete - as it should be.
Problem:
The total time is roughly a multiple of threadnum , that is to say the execution time increases ?
(Unfortunately I cannot run a C# Profiler at the moment. Additionally I would prefer C# 3 code compatibility. )
Others are likely struggling as well. It may be that the VS 2010 express edition vshost process stacks and schedules threads to be run sequentially?
Another MT-performance issue was posted recently posted here as "Visual Studio C# 2010 Express Debug running Faster than Release":
Code:
public int threadnum = 8;
Thread[] threads = new Thread[threadnum];
Stopwatch stpwtch = new Stopwatch();
stpwtch.Start();
for (var threadidx = 0; threadidx < threadnum; threadidx++)
{
threads[threadidx] = new Thread(BuildDictionary);
threads[threadidx].Start(threadidx);
threads[threadidx].Join(); //Blocks the calling thread, till thread completion
}
WriteLine("Total - time: {0} msec", stpwtch.ElapsedMilliseconds);
Can you help please?
Update:
It appears that the strange behavior of an almost linear slowdown with increasing thread-number is an artifact due to the numerous hooks of the IDE's Debugger.
Running the process outside the developer environment, I actually do get a 30% speed increase on a 2 logical/physical core machine. During debugging I am already at the high end of CPU utilization, and hence I suspect it is wise to have some leeway during development through additional idle cores.
As initially, I let each thread compute on its own local data-chunk, which is locked and written back to a shared list and aggregated after all threads have finished.
Conclusion:
Be heedful of the environment the process is running in.
We can put the dictionary synchronization issues Tony the Lion mentions in his answer aside for the moment, because in your current implementation you are in fact not running anything in parallel!
Let's take a look at what you are currently doing in your loop:
Start a thread.
Wait for the thread to complete.
Start the next thread.
In other words, you should not be calling Join inside the loop.
Instead, you should start all threads as you are doing, but use a singaling construct such as an AutoResetEvent to determine when all threads have completed.
See example program:
class Program
{
static EventWaitHandle _waitHandle = new AutoResetEvent(false);
static void Main(string[] args)
{
int numThreads = 5;
for (int i = 0; i < numThreads; i++)
{
new Thread(DoWork).Start(i);
}
for (int i = 0; i < numThreads; i++)
{
_waitHandle.WaitOne();
}
Console.WriteLine("All threads finished");
}
static void DoWork(object id)
{
Thread.Sleep(1000);
Console.WriteLine(String.Format("Thread {0} completed", (int)id));
_waitHandle.Set();
}
}
Alternatively you could just as well be calling Join in the second loop if you have references to the threads available.
After you have done this you can and should worry about the dictionary synchronization problems.
A Dictionary can support multiple readers concurrently, as long as the collection is not modified. From MSDN
You say:
but a shared dictionary worked fine, without locking.
Each thread increments the values contained in a shared dictionary
Your program is by definition broken, if you alter the data in the dictionary without proper locking, you will end up with bugs. Nothing more needs to be said.
I wouldn't use some shared static Dictionary, if each thread worked on a local copy you could amalgamate your results once all threads had signalled completion.
WaitHandle.WaitAll avoids any deadlocking on an AutoResetEvent.
class Program
{
static void Main()
{
char[] text = "Some String".ToCharArray();
int numThreads = 5;
// I leave the implementation of the next line to the OP.
Partition[] partitions = PartitionWork(text, numThreads);
completions = new WaitHandle[numThreads];
results = IDictionary<char, int>[numThreads];
for (int i = 0; i < numThreads; i++)
{
results[i] = new IDictionary<char, int>();
completions[i] = new ManualResetEvent(false);
new Thread(DoWork).Start(
text,
partitions[i].Start,
partitions[i].End,
results[i],
completions[i]);
}
if (WaitHandle.WaitAll(completions, new TimeSpan(366, 0, 0, 0))
{
Console.WriteLine("All threads finished");
}
else
{
Console.WriteLine("Timed out after a year and a day");
}
// Merge the results
IDictionary<char, int> result = results[0];
for (int i = 1; i < numThreads - 1; i ++)
{
foreach(KeyValuePair<char, int> item in results[i])
{
if (result.ContainsKey(item.Key)
{
result[item.Key] += item.Value;
}
else
{
result.Add(item.Key, item.Value);
}
}
}
}
static void BuildDictionary(
char[] text,
int start,
int finish,
IDictionary<char, int> result,
WaitHandle completed)
{
for (int i = start; i <= finish; i++)
{
if (result.ContainsKey(text[i])
{
result[text[i]]++;
}
else
{
result.Add(text[i], 1);
}
}
completed.Set();
}
}
With this implementation the only variable that is ever shared is the char[] of the text and that is always read only.
You do have the burden of merging the dictionaries at the end but, that is a small price for avoiding any concurrencey issues. In a later version of the framework I would have used TPL and ConcurrentDictionary and possibly Partitioner<TSource>.
I totally agree with TonyTheLion and others, and as you fix the actual problem with join'ing at the wrong place, there still will be problem with (no) locks and updating the shared dictionary. I wanted to drop you a quick workaround: just wrap your integer value into some object:
instead of:
Dictionary<uint, int> dict = new Dictionary<uint, int>();
use:
class Entry { public int value; }
Dictionary<uint, Entry> dict = new Dictionary<uint, Entry>();
and now increment the Entry::value instead. That way, the Dictionary will not notice any changes and it will be safe without locking the dictionary.
Note: this will however work only if you are guaranteed if one thread would use only its own one Entry. I've just noticed this is not true as you said 'histogram of characters'. You will have to lock over each Entry during the increment, or some increments may be lost. Still, locking at Entry layer will speed up signinificantly when compared to locking at whole dictionary
Roem saw it.
Your main thread should Join the X other Threads after having started all of them.
Else it waits for the 1st thread to be finished, to start and wait for the 2nd one.
for (var threadidx = 0; threadidx < threadnum; threadidx++)
{
threads[threadidx] = new Thread(BuildDictionary);
threads[threadidx].Start(threadidx);
}
for (var threadidx = 0; threadidx < threadnum; threadidx++)
{
threads[threadidx].Join(); //Blocks the calling thread, till thread completion
}
As Rotem points out, by joining in the loop you are waiting for each thread to complete before going continuing.
The hint for why this is can be found on the Thread.Join documentation on MSDN
Blocks the calling thread until a thread terminates
So you loop will not continue until that one thread has completed it's work. To start all the threads then wait for them to complete, join them outside the loop:
public int threadnum = 8;
Thread[] threads = new Thread[threadnum];
Stopwatch stpwtch = new Stopwatch();
stpwtch.Start();
// Start all the threads doing their work
for (var threadidx = 0; threadidx < threadnum; threadidx++)
{
threads[threadidx] = new Thread(BuildDictionary);
threads[threadidx].Start(threadidx);
}
// Join to all the threads to wait for them to complete
for (var threadidx = 0; threadidx < threadnum; threadidx++)
{
threads[threadidx].Join();
}
System.Diagnostics.Debug.WriteLine("Total - time: {0} msec", stpwtch.ElapsedMilliseconds);
You will really need to post your BuildDictionary function. It is very likely that the operation will be no faster with multiple threads and the threading overhead will actually increase execution time.
I have two secondary threads,that executes the method WriteX as described below:
static void Main(string[] args)
{
ThreadStart one = new ThreadStart(WriteX);
Thread startOne = new Thread(one);
ThreadStart two = new ThreadStart(WriteX);
Thread startTwo = new Thread(two);
startOne.Start();
startTwo.Start();
Console.ReadKey(true);
}
public static void WriteX()
{
for (int i = 1; i <= 3; i++)
{
Console.WriteLine("Hello");
Thread.Sleep(1000);
}
}
( 1 ) How can i find the time taken (in milli seconds) by "startOne" and "startTwo" to complete its task?
( 2 ) I started the Thread (Start() ).Won't i need to stop the thread upon successful execution or will that be handled by the primary thread (Main() in this case) ?
(3) How can i print the message say , startOne is executing WriteX() method and startTwo is executing WriteX() method ?
In every serious logging framework, you can include the thread Id, or the thread name in the output message. If you just want to use the Console, you may have to manually append the current thread id/name to your message.
Like so:
public static void WriteX()
{
var sw = Stopwatch.StartNew();
for (int i = 1; i <= 3; i++)
{
Console.WriteLine("Hello");
Thread.Sleep(1000);
}
sw.Stop();
Console.WriteLine("{0} {1}",
Thread.CurrentThread.ManagedThreadId,
sw.Elapsed);
}
To name a thread, just use the Name property on the thread instance:
ThreadStart one = new ThreadStart(WriteX);
Thread startOne = new Thread(one);
startOne.Name = "StartOne";
//...
Console.WriteLine("{0} {1}", Thread.CurrentThread.Name, sw.Elapsed);
Also, when the method you pass as ThreadStart constructor argument finishes, the thread ends automatically. Use the Join method if you want to wait for a thread end in the main one.
Wrap WriteX() in something that saves the current time before and after (or simply do it at the begin and end of WriteX()) and print the difference at the end.
When the method passed to ThreadStart finishes, the thread terminates itself (and cleans itself up)
Again, wrap WriteX() in something that does the logging. I suggest to look at the source for ThreadStart to see how it's implemented and how you can extend/wrap it.
If you have no idea what I'm talking about, this article might help.
It depends on whether you want to get sum of execution times of two independent tasks or the biggest one. In first case you need to measure required time within WriteX. In the second case before startOne and stop measuring when both ar finished. Use Stopwatch in both cases.
Use Thread.Join to wait until both threads are finished.
Use parameterized start Thread.Start(object) and pass task name there.