|
Note: This is an unedited contribution. If this article is inappropriate,
needs attention or copies someone else's work without reference then please
Report This Article

Introduction
Multithreading has always impressed me. Being able to do lots of things at once is really impressive, but we can’t do this if we don’t have the proper hardware. Till now, all we could do is separate the hard CPU work in a background thread and thus leave the user interface unblocked. I wanted to go further than this, exploiting the newest capabilities of new CPUs (at user’s hands) and try to get a real working multithreading example, that is, with more than one thread running in the background.
That is what this article is all about, and have to say, that the results have impressed me. Hope you will find it interesting. Well, in a multi cpu server with 4 CPU’s the benefits are of 280% (in a cpu-intensive job) and on normal machines with non-cpu intensive jobs it can go from 500% to 1000% in performance improvements…
Background
There are a lot of introductory articles to multithreading for .Net 2.0 and, have to say, they have helped me a lot. What I have used is the BackgroundWorker .Net 2.0 component (but there are code implementations for it on Net 1.1 that do the job. Here I put some links to these articles:
Good introductory article from Paul Kimmel. [^]
Other introductory article, from Juval Löwy[^]
impressive site of Joseph Albahari dedicated almost to threading in c#... a must see…[^]
Safe, Even Simpler Multithreading in Windows Forms 2.0, from Michael Weinhardt, I took the cpu-intensive job from his web. Well, he took it from Chris Sell’s article (he refers him – as I do ^_^)[^]
Well these are must read articles if you’re new to the threading world or if you’re not but want to get updated with the new Net 2.0’s BackgroundWorker component..
Problems addressed
Any kind of problem… being it a processor intensive or a normal task:
Processor intensive task : It can be divided into one, two or more threads which will be used by a CPU each (multiplying the performance by each processor..)
Normal task : Each “normal” task if done sequentially has a “delay” whenever it accesses the file system for reading or writing data, if it gets access to data storage or if it uses a web service… all of this means time that normally is unused and counts as a delay for the user response or the task itself. With a multiple threaded job management, there will be no delay. These time will be assigned and used by parallel tasks and used, not lost. That will result in, per example on a 100 package with 100ms of delay each the performance difference of a 1 thread model to a 20thread model was about 1000% in performance.
Let’s say that if the problem is building the blocks of a web site page, instead of doing these sequentially , taking 1-4 seconds into having all the sections built; the banner, the users online, the last articles, the most voted tools, etc… what if we could build all of these asynchronously and when they’re built up send them to the user? We will save the webservice calls, the database calls and a lot of other precious time… and more on! These calls would be serviced faster and that would mean that the possibilities of coinciding the calls would be reduced, increasing the response times substantially. Interesting?
The solution is already here..
It is called BackgroundWorker and for our intentions we will subclass it. Background worker helps us to set-up a “Worker” for doing a work in an asynchronous way. What we want to do is set up a Factory (oops, no design patterns meaning here) where one kind of job will be done, that will mean thaw we will have a kind of job, some process, and some workers that know how to do this job. Of course, we will need a manager for assigning the jobs to the workers and what to do when they reach a step of the job and when they finish it. And yes, also we want the manager to be able to speak to the workers to stop. They have to take a rest too! And when the manager says so, they must stop! We will explain thins from bottom to top, beginning from the Worker and then we will see the Manager.
The Worker
It is a subclass of the Background worker, we set up the constructor to assign to true two properties of BackgroundWorker that are WorkerReportsProgress and WorkerSupportsCancellation which will enable us to do what the names say, report progress, normally to an UI and cancel the job (and subsequently all jobs) if they take too long. We also assign a id number to each worker. The manager needs to control them, though. Here’s the code: public class MTWorker : BackgroundWorker
{
#region Private members
private int _idxLWorker = 0;
#endregion
#region Properties
public int IdxLWorker
{
get { return _idxLWorker; }
set { _idxLWorker = value; }
}
#endregion
#region Constructor
public MTWorker()
{
WorkerReportsProgress = true;
WorkerSupportsCancellation = true;
}
public MTWorker(int idxWorker)
: this()
{
_idxLWorker = idxWorker;
}
#endregion
Also, we will override another of the BackgroundWorker’s methods. In fact the most interesting one, which does the real Job. And it means it. It’s name is OnDoWork and it is the method that is called when we invoke or launch the multithreaded task. Here we manage the start-up of the task, its progress, its cancellation and its completion. I have added two possible jobs to do, one “Normal” that emulates with delay’s the waiting time of a non cpu-inensive task with has to ask and wait for filesystem, network, database or webservices calls… and other which is a CPU intensive Job: Calculating the PI number. You can play with it and see the results of giving more or less delay and increasing the thread’s number (Oops, I meant the worker’s numbers…). Here is the OnDoWork code: protected override void OnDoWork(DoWorkEventArgs e)
{
int digits = (int)e.Argument;
double tmpProgress = 0;
int Progress = 0;
String pi = "3";
this.ReportProgress(0, pi);
Boolean bJobFinished = false;
int percentCompleteCalc = 0;
String TypeOfProcess = "NORMAL"; while (!bJobFinished)
{
if (TypeOfProcess == "NORMAL")
{
#region Normal Process simulation, putting a time
delay to emulate a wait-for-something
while (!bJobFinished)
{
if (CancellationPending)
{
e.Cancel = true;
return; }
Thread.Sleep(250);
percentCompleteCalc = percentCompleteCalc + 10;
if (percentCompleteCalc >= 100)
bJobFinished = true;
else
ReportProgress(percentCompleteCalc, pi);
}
#endregion
}
else
{
#region Pi Calculation - CPU intensive job,
beware of it if not using threading ;) !!
if (digits > 0)
{
pi += ".";
for (int i = 0; i < digits; i += 9)
{
int nineDigits = NineDigitsOfPi.StartingAt(i + 1);
int digitCount = System.Math.Min(digits - i, 9);
string ds = System.String.Format("{0:D9}", nineDigits);
pi += ds.Substring(0, digitCount);
tmpProgress = (i + digitCount);
tmpProgress = (tmpProgress / digits);
tmpProgress = tmpProgress * 100;
Progress = Convert.ToInt32(tmpProgress);
ReportProgress(Progress, pi);
if (CancellationPending) {
bJobFinished = true;
e.Cancel = true;
return;
}
}
}
bJobFinished = true;
#endregion
}
}
ReportProgress(100, pi); e.Result = pi; }
The Manager
Here is what has more fun and I am pretty sure that it can be improved a lot – any comment or improvement are welcome! What it does is to generate and configure a Worker for each Thread and then it assigns the jobs to them. By now the only parameter it passes to the worker is a number, but it could pass a class or struct with all the job definition… A possible upgrade would be to implement a strategy pattern here for choosing how to do the internal job. Well we then call the InitManager which configures the jobs, its number, the specs of the jobs to do and then creates an array of MultiThread Workers and configures them. The configuration code follows. private void ConfigureWorker(MTWorker MTW)
{
MTW.ProgressChanged += MTWorker_ProgressChanged;
MTW.RunWorkerCompleted += MTWorker_RunWorkerCompleted;
}
Like this, the Worker’s subclassed thread management Methods are linked to the Methods held by the Manager. Note that with a Strategy pattern implemented we could assign these to the proper manager for these methods. Then we have the most important method, the AssignWorkers. What it does is to check all the workers and if there is anyone that is not working it assigns a job to it. That is, if there are jobs left to process. When it finishes checking workers, if we found that there is no worker working (and thus we have not assigned any job too) that will mean the end of the jobs. No more to do, all thing’s done! Here’s the code: public void AssignWorkers()
{
Boolean ThereAreWorkersWorking = false;
foreach (MTWorker W in _arrLWorker)
{
if (W.IsBusy == false)
{
if (_iNumJobs > _LastSentThread)
{
_LastSentThread = _LastSentThread + 1;
W.JobId = _LastSentThread; W.RunWorkerAsync(_iPiNumbers); ThereAreWorkersWorking = true;
}
}
else
{
ThereAreWorkersWorking = true;
}
}
if (ThereAreWorkersWorking == false)
{
Button BtnStart = (Button)FormManager.Controls["btnStart"];
Button BtnCancel = (Button)FormManager.Controls["btnCancel"];
BtnStart.Enabled = true;
BtnCancel.Enabled = false;
MessageBox.Show("Hi, I'm the manager to the boss (user): " +
"All Jobs have finished, boss!!");
}
}
We call this method whenever a job is finished. This way it ensures the completion of all jobs. We also link the form through a property of this class so we can associate it to any form we want. Well we could want to link it to another class, but this is the most normal thing to do. Well… improving it we could get a BackgroundManager for all our application needs..
The UI
Last but not less important, we link all this to a form. The code is minimal and it’s pretty simple: we add a reference to the Manager, we configure it on the form’s constructor and on a start button we call the Manager’s LaunchManagedProcess. private MTManager LM;
public Form1()
{
InitializeComponent();
LM = new MTManager(this, 25);
LM.InitManager();
}
private void btnStart_Click(object sender, EventArgs e)
{
btnStart.Enabled = false;
btnCancel.Enabled = true;
LM.LaunchManagedProcess();
}
private void btnCancel_Click(object sender, EventArgs e)
{
LM.StopManagedProcess();
btnCancel.Enabled = false;
btnStart.Enabled = true;
}
Trying it!
This is the funniest part, changing the properties of how many threads to run simultaneously and how many Jobs to be processed and then try it on different CPU’s… ah, and of course, change the calculation method from a CPU-intensive task to a normal task with a operation delay... I would love to know your results and what have you done with this, any feedback would be great!!
Exercises For You…
This is not done! It could be a MultiThreadJob Framework if there is being done the following:
Implement a Strategy pattern that determines the kind of Worker to produce (with a factory pattern) so we will be able to do different kind of jobs inside the same factory.. what about migrating a database and processing each table in a different way… or integrating systems with this engine…
Implement -or extend- the strategy pattern for determining the treatment for the Input data and the result data of the jobs. We could too set-up a factory for getting the classes into a operating environment.
Optimize the AssignWorkers engine – I am pretty sure it can be improved.
Improve the WorkerManager class in order to be able to attach it to another class instead to only a form.
Send me the code! I Would love to hear from you and what have you done.
| You must Sign In to use this message board. |
|
| | Msgs 1 to 25 of 25 (Total in Forum: 25) (Refresh) | FirstPrevNext |
|
|
 |
|
|
 |
|
|
I am new to MT in .NET. You article is pretty good. But i dint get 100%of it (might be because i am new to MT) My requirement is to read the files (20 files) from the Loaction & parse it for values & dump the values in DB. Just wanted to know .. 1. where do i write the code to pool for the file. 2. I have written method to parse the file, where do i call this method. 3. Also if i have say 10 threads, reading 10 files at a time, if i update the values in DB with the threads then 10 DB connection will be open at a time. What are the pros & cons of this?
It will be great if you can thro some light on the points above. By the time i'll read more about the threading.
Thnx & Rgds, Alok Kumbhat
|
| Sign In·View Thread·PermaLink | |
|
|
|
 |
|
|
 |
|
|
Why the specific tasks in the "OnDoWork" method of MTWorker? It makes no sense. MTWorker should be generic. Now it's useless. Everyone who wants to use your code should rewrite the half of the code in this article.
Why do you use "Manager" and "Worker" as class name, if they do very specific things like calculating PI and interacting with "btnCancel", "lbl_...", ... etc?
Just for your effort I give you a 2. Sorry
|
| Sign In·View Thread·PermaLink | |
|
|
|
 |
|
|
Couldn't agree more. Lots of talk of patterns. And yet such an obvious error. 
On the other hand BackgroundWorker is intended for just this sort of level of coding. Keep it simple. And heck... it's not the worst peice of code I've ever seen.
|
| Sign In·View Thread·PermaLink | 1.00/5 (1 vote) |
|
|
|
 |
|
|
Man!!
Looks like I can't post sample code that is not fully decoupled, optimized or whatever...
it is just a sample that does show a technique, not a perfect artwork... it doesn't intend to be perfect..
The intention is clarity... I could setup easily a strategy there but the samples wouldn't be so clear, don't you think?
The intention was what you said in your last point, Keep it simple. And also clear.
I don't understand why you get so upset about that point, it's like I jumped a red light or somewhat... but hey that was on purpose!!
Hey it is curious, one says it is the best example of multithreading with ui and others say it isn't the worst piece of code ever seen... quite a controversy, isn't it?
Anyway I did this cause I didn't found any example or article of a ui with many threads in sync.. this is it and it works and even if my language is not english it's not so bad 
Regards & thanks for your comments, Jose
|
| Sign In·View Thread·PermaLink | |
|
|
|
 |
|
|

Hi Arash,
Thanks for your comment, have seen you have logged on just to post your comment to me. Anyway, the "sense" is to make clear how to use the backgroundworker, meaning to show the techniques.
I'm not trying here to show-off the best design on the world. Anyway, it does work and it does it finely. and more important, it shows how to apply backgroundworker for multiple tasks.
I do agree with you that this is not the best code on the flexibility or coupling point of view. It didn't intend to be... nor to be a Open Source project, so sorry if you were looking for that, it is not that. It is just a sample for an article... not how to implement a full arquitecture that maybe was what you were looking for...
As for your first point, the tasks are on the OnDoWork for clarity, just that... That was my intention and please forgive me if you felt insulted, didn't intend that..
If you feel generous about that it would be nice to see a improvement on the code to make it more decoupled from the tasks implementation, that would be quite constructive from your side and I would appreciate it.
About the names of the classes, these are just for being clear to the reader, just that. The specific things are ther just "examples" to show what can be done, nothing more...
Don't take it too serious cause it feels like I have insulted or done something to you... hopefully I don't.. comments like that just make me to stop collaborating with the community or even writting... quite destructive I think...
|
| Sign In·View Thread·PermaLink | |
|
|
|
 |
|
|
Hi, I must say this may be just what the doctor ordered. I'm very new to .NET and extremely new to MT concepts. I'm currently writing a VB winform app that we want to try and use MT to "talk" or collect data from several instruments connected via serial ports in a multi-threaded way. The old DOS program they had cycled through the ports sequentially using a MUX device and was very slow. We want to use USB to Serial and attempt to read/write to all (up to 16) instruments at the same time, reporting progress and allowing the user to intervene on any given task (abort, re-run, etc.) Therefore, I could see having a cancel and re-run button sitting next to each "progress" bar and possibly also having text displayed showing what is going on on each active port.
So, these methods that will be called I think fall under "chunky" as they need to send enq, wait for ack, loop on buffer reads, etc. all to build/return one big string. I already have the main logic done for communication but I just need to figure out the best way to "wrap" it using the UI and MT handler.
This example seems like it may be a good start, but I think I would like to go ahead and convert to VB in order to avoid needing to build an assembly to use.
Do you know of any issues I may run into? Any other suggestions or updates to this "framework" would be appreciated.
Thanks, Randy Jean
|
| Sign In·View Thread·PermaLink | 5.00/5 (1 vote) |
|
|
|
 |
|
|
Hi Randy,
Thanks for your opinion, if you also vote me that would make me even happier  As long as I can tell I have used this method to do some calculus intensive tasks, well with the example provided you can check that for 16 threads (instruments) to do a somewhat "do something and wait"...
Other side of it is that maybe this is not the best arquitecture for what you're trying to do... I mean that with .Net you have eventlisteners or delegates which help to implement the engine you're trying to build. I think, for what you're telling that a Observer pattern would fit better there or even a MVC Pattern (Model View Controller) pattern...
If you want to make the instruments "sing" or be controlled, yes, this multithreading framework will do it's own use but if what you want is to "hear and comunicate" with your instruments based on the events (a note is played) any of these two patterns will do the job more efficiently...
I am sorry as this "framework" was developed mainly for this example and I only have used it for a personal project in which is very tightly coupled with the functionality of the application.. and still under development..
Anyway, feel free to ask. Anything I might be able to help I'll try to answer.
Regards, Jose
|
| Sign In·View Thread·PermaLink | |
|
|
|
 |
|
|
I must agree with Randy that this is one best example i've found i.e. multi-threading having to interface with progressbar. Similar as Randy, I intend to write a program that reads a directory of files and process/search required text within. Threads can be configured up to 25.
I've converted to vb.net 2005. Unfortunately, stumbled upon error at below lines.
Private Sub ConfigureWorker(ByVal MTW As MTWorker) 'We associate the events of the worker MTW.ProgressChanged += MTWorker_ProgressChanged() MTW.RunWorkerCompleted += MTWorker_RunWorkerCompleted() End Sub
'Public Event ProgressChanged(sender As Object, e As System.ComponentModel.ProgressChangedEventArgs)' is an event, and cannot be called directly. Use a 'RaiseEvent' statement to raise an event.
Jose, can you help?
Thank you.
|
| Sign In·View Thread·PermaLink | |
|
|
|
 |
|
|
Hi, the event assignment in vb is a little different (i think is better than c#)
AddHanlder MTW.ProgressChanged , AddressOf MTWorker_ProgressChanged AddHanlder MTW.RunWorkerCompleted, AddressOf MTWorker_RunWorkerCompleted
Cheers
|
| Sign In·View Thread·PermaLink | |
|
|
|
 |
|
|
Hello, I wanted to download this project and take a look at it. It seems to be missing the zip file to download. Is this only temporary?
Thanks,
Eric
A Life? Cool! Where can I download one of those!
|
| Sign In·View Thread·PermaLink | |
|
|
|
 |
|
|
Hi!
please try it again as I have no problem downloading from the link...
if you can, give me your mail and I will send it to you..
Sorry for the trouble, all I can say is that I tested it and worked fine..
Regards, Jose
|
| Sign In·View Thread·PermaLink | 1.00/5 (1 vote) |
|
|
|
 |
|
|
 |
|
|
Hi,
I tried many times to download your demo, but still got the error page. Could you send it to me at _leyon@163.com_ ? Thanks a lot.
Regards, Leyon
|
| Sign In·View Thread·PermaLink | |
|
|
|
 |
|
|
This sounds like something a marketer would say. Having a thousand threads won't make the application a million times faster. It is strictly dependent on the code, and the number of processors that are on the machine. Some applications don't multithread well, and you may get worst performance rather than increased performance.
ROFLOLMFAO
|
| Sign In·View Thread·PermaLink | 3.67/5 (2 votes) |
|
|
|
 |
|
|
Hi, ROFLOLMFAO... Certainly true what you say,
but, if you read the article, on the first lines I say that it depends on the hardware and of the kind of process to be done ...
I attach the text here...
"I wanted to go further than this, exploiting the newest capabilities of new cpu’s (at user’s hands) and try to get a real working multithreading example, that is, with more than one thread running in the background.
That is what this article is all about, and have to say, that the results have impressed me. Hope you will find it interesting. Well, in a multi cpu server with 4 CPU’s the benefits are of 280% (in a cpu-intensive job) and on normal machines with non-cpu intensive jobs it can go from 500% to 1000% in performance improvements…"
That's why I have set-up two kinds of job for being processed. If you look at the code, you will see I have 2 jobs, one cpu intensive and other normal in which you can set-up a delay to emulate any kind of job.
Of course the performance improvements depend on your physical architecture... and I haven't said a milion times faster, just that a cpu intensive process gets 280% improvement, that is the test of PI calculation in a 2 Xeon cpu server..
I'm not saying this because of marketing but of real tests I have done cause I was curious  
Of course, as everything, it will need a careful load balancing depending on the architecture and jobs to be done... it is a delicated kind of programming till now only done in supercomputing sites... that's what excited me about it 
That, anyway has enough for another article... if it is voted enough maybe I will exploit the load balancing and the places this "technique" can be exploited with success..
Thanks for your comments!
-- modified at 4:38 Monday 6th November, 2006
|
| Sign In·View Thread·PermaLink | 2.00/5 (1 vote) |
|
|
|
 |
|
|
Hi Jose,
I think the area you are trying to explain may have a great value if you would put little more attention to the content of the article..
I am not criticizing you, I appreciate your effort but with out telling more let me show you a one place (there are so many), where I think I cannot understand what you are trying to say.. The wording needs drastic improvement (I know most of us who write at Code Project are not good technical writers)... It is just very difficult to get the idea..
I think the provided samples are technically wrong, even though it can be used to explain them, no one who knows little bit about technology will not use them. As an example when you say..
Let’s say that if the problem is building the blocks of a web service, instead of doing these sequentially.
What you mean by this? ->blocks of a web service
taking 1-4 seconds into having all the sections built, the banner, the users online, the last articles, the most voted tools, etc…
I think if we are using web services as the back end of a web site, I do not think that it is a good way to use thread, you will just create more problems by going to solve a one problem.. Second, make your web service call chunky appose to chatty (what you do is chatty). As I said these are some of technically wrong samples.. I mean other than not understandable wording..
So, let's give it another try and improve it..
L.W.C. Nirosh, Colombo, Sri Lanka.
|
| Sign In·View Thread·PermaLink | |
|
|
|
 |
|
|
Yes, it can be true, it is my first article, sorry if I done something wrong… this is a very professional community and I was hoping to be at “the level”. Anyway my mother language is not English, so sorry if I made some mistakes…
About what you say with "With blocks of a web service" I was meaning "web site"… sorry for that! And thanks for indicating it.
I was meaning to build that blocks on the web server, not through a web service… anyway the time and weather normally will go through a web service and is a good thing to go in first place asincronously… as it is a process that will take most of the time that the other process that build the web blocks locally.
I do not understand what you’re saying with “make your web service call chunky appose to chatty (what you do is chatty)”.. if you explain it I can correct it…
Thanks for your words, will revise it for any other word or misunderstanding…
Jose Luis Latorre
-- modified at 4:28 Monday 6th November, 2006
|
| Sign In·View Thread·PermaLink | 2.00/5 (1 vote) |
|
|
|
 |
|
|
Jose Luis Latorre wrote: Anyway my mother language is not English, so sorry if I made some mistakes……
Mine also not, so I do mistakes too.. 
Jose Luis Latorre wrote: I do not understand what you’re saying with “make your web service call chunky appose to chatty (what you do is chatty)”.. if you explain it I can correct it…
Chunky Call: A chunky call is a function call that performs several tasks, such as a method that initializes several fields of an object. This is to be viewed against chatty calls, which do very simple tasks and require multiple calls to get things done (such as setting every field of an object with a different call). It's important to make chunky, rather than chatty calls across methods where the overhead is higher than for simple, intra-AppDomain method calls. P/Invoke, interop and remoting calls (Web service etc) all carry overhead, and you want to use them sparingly. In each of these cases, you should try to design your application so that it doesn't rely on small, frequent calls that carry so much overhead.
L.W.C. Nirosh, Colombo, Sri Lanka.
|
| Sign In·View Thread·PermaLink | |
|
|
|
 |
|
|
Hi Nirosh,
thanks for the explanation I didn't understand the term.
I really don't think I'm making chatty calls, anyway this is a demo of the multithreading. In a real world situation, the Worker class would be subclassed or applied a strategy pattern to do some task in a chunky, performance-obsessive way .
The small-frequent calls you're talking about (I think you refer to the OnDoWork method) are there because of showing progress and make the example nicer. just that.
If not, please say so, I'm the most interested into knowing any possible failure of my code.
Thanks!! Jose
|
| Sign In·View Thread·PermaLink | |
|
|
|
 |
|
|
Jose Luis Latorre wrote: I'm the most interested into knowing any possible failure of my code.
This indicate me that I should not really bother about the technical correctness of given examples.. In a way correct/ I agree but...
L.W.C. Nirosh, Colombo, Sri Lanka.
|
| Sign In·View Thread·PermaLink | |
|
|
|
 |
|
| |