Multithreading Tutorial - CodeProject

http://www.codeproject.com/KB/threads/MultithreadingTutorial.aspx

7,248,533 members and growing! (18,507 online)

Abhishek Basak

Sign out

Home

Articles

Questions & Answers

Learning Zones

Jobs

Features

Help!

Lounge

threads

Sponsored Links

Article Browse Code Stats Revisions
» General Programming » Threads, Processes & IPC » Multi-threading

30

4.40 / 5, 41 votes

Multithreading Tutorial
By John Kopplin | 28 Dec 2006 This article demonstrates how to write a multithreaded Windows program in C++ using only the Win32 API. Download source and demo projects - 425 KB

See Also...

Background
When you run two programs on an Operating System that offers memory protection, as Windows and UNIX/Linux do, the two programs are executed as separate processes, which means they are given separate address spaces. This means that when program #1 modifies the address 0x800A 1234 in its memory space, program #2 does not see any change in the contents of its memory at address 0x800A 1234. With simpler Operating Systems that cannot accomplish this separation of processes, a faulty program can bring down not only itself but other programs running on that computer (including the Operating System itself). The ability to execute more than one process at a time is known as multi-processing. A process consists of a program (usually called the application) whose statements are performed in an independent memory area. There is a program counter that remembers which statement should be executed next, and there is a stack which holds the arguments passed to functions as well as the variables local to functions, and there is a heap which holds the remaining memory requirements of the program. The heap is used for the memory allocations that must persist longer than the lifetime of a single function. In the C language, you use malloc to acquire memory from the heap, and in C++, you use the new keyword. Sometimes, it is useful to arrange for two or more processes to work together to accomplish one goal. One situation where this is beneficial is where the computer's hardware offers multiple processors. In the old days this meant two sockets on the motherboard, each populated with an expensive Xeon chip. Thanks to advances in VLSI integration, these two processor chips can now fit in a single package. Examples are Intel's "Core Duo" and AMD's "Athlon 64 X2". If you want to keep two microprocessors busy working on a single goal, you basically have two choices: 1. design your program to use multiple processes (which usually means multiple programs), or 2. design your program to use multiple threads. So, what's a thread? A thread is another mechanism for splitting the workload into separate execution streams. A thread is lighter weight than a process. This means, it offers less flexibility than a full blown process, but can be initiated faster because there is less for the Operating System to set up. What's missing? The separate address space is what is missing. When a program consists of two or more threads, all the threads share a single memory space. If one thread modifies the contents of the address 0x800A 1234, then all the other threads immediately see a change in the contents of their address 0x800A 1234. Furthermore, all the threads share a single heap. If one thread allocates (via malloc or new) all of the memory available in the heap, then attempts at additional allocations by the other threads will fail. But each thread is given its own stack. This means, thread #1 can be calling

Announcements

The Daily Insider

FunctionWhichComputesALot() at the same time that thread #2 is calling FunctionWhichDrawsOnTheScreen(). Both of these functions were written in the same program.
There is only one program. But, there are independent threads of execution running through that program. What's the advantage? Well, if your computer's hardware offers two processors, then two threads can run simultaneously. And even on a uni-processor, multi-threading can offer an advantage. Most programs can't perform very many statements before they need to access the hard disk. This is a very slow operation, and hence the Operating System puts the program to sleep during the wait. In fact, the Operating System assigns the computer's hardware resources to somebody else's program during the

1 of 8

8/14/2010 2:49 AM

This is what I will demonstrate. Unfortunately. This is probably the right time to read Jaeschke's original articles. The source code I present could also be written in standard C. You're probably familiar with Java and C#. I sat down and rewrote his programs in standard C++. The first is the name of the function which you want the new thread to begin executing. that I think this language will disappear. and the only requirements are that it take a single passed parameter (of type void*) and that it returns nothing. Creating Threads Under Windows It is unfortunate that the C++ language didn't standardize the method for creating threads. Dobb's Portal website. But. I am not a fan of this approach. reentrancy. This stands for "C++ Common Language Infrastructure" and is a Microsoft invention. This will be made clear by the following example program: Collapse 2 of 8 8/14/2010 2:49 AM . and posted on the Internet at sites such as CodeProject. unsigned stack_size. In the October 2005 issue. which forces the Windows Operating System to select the stack size for me. void *arglist ). he wrote his follow-up article entitled "C++/CLI Threading: Part 2". but using it is easy. If you are writing a program to run under Windows. In fact. Dobb's Journal. The _beginthread() function takes three passed parameters. I am sure Java and C# are going to hang around. The Win32 API offers the following function to create a new thread: Collapse uintptr_t _beginthread( void( __cdecl *start_address )( void * ). The Jaeschke Magazine Articles One good way to learn any new programming concept is to study other people's code. I especially liked how his example programs were short and yet displayed data corruption when run without the synchronization methods that are required for successful communication between threads. That is what is meant by the function signature: Collapse void( __cdecl *start_address )( void * ). and I haven't had any problems with this approach. The URLs where you can find Jaeschke's two articles are given above. by Rex Jaeschke. that's easier than accomplishing it in C++ for a reason we will get to in just a minute. The second passed parameter to the _beginthread() function is a requested stack size for the new thread (remember. and in the November 2005 issue. This is what I am sharing with you now. You get to write this function. then you will want to use the Win32 API to create your threads. which are two languages that offer managed code where the Operating System rather than the programmer is responsible for deallocating all memory allocations made from the heap. each thread gets its own stack). But.aspx wait. This function signature might look intimidating. so I wasn't very interested in Jaeschke's original source code.codeproject. which is associated with Dr. C++/CLI is Microsoft's proposal to add managed code to the C++ language.CodeProject http://www. if you have written a multi-threaded program. another excellent programming magazine. various compiler vendors invented their own solutions. But. the C/C++ Users Journal magazine folded shortly after these articles appeared. which is already a very complicated language. but C++/CLI attempts to add so many new notations (and concepts) on top of C++. your other threads can continue.com/KB/threads/MultithreadingTutorial. For example. I came across some good examples of multi-threaded programs in two articles written for the C/C++ Users Journal. etc. the original articles and Jaeschke's source code are still available at the following websites: Part 1 Part 2 You'll notice that the content from the defunct C/C++ Users Journal has been integrated into the Dr. However. I don't plan to explain how a program is given its first thread automatically and all additional threads must be created by explicit actions by the program (oops). You might not be familiar with the notation C++/CLI. I always set this parameter to 0. This is called the thread's entry-point-function. atomicity. then when one of your threads stalls. I still read the original C/C++ Users Journal article and thought Jaeschke had selected good examples of multi-threading. Jaeschke wrote an article entitled "C++/CLI Threading: Part 1". The final passed parameter to the _beginthread() function is the single parameter you want passed to the entry-point-function.Multithreading Tutorial . Therefore. since I don't plan to repeat his great explanations of multitasking. You can find source code in magazine articles. So.

(INT_PTR)arg ) . we use a two-step procedure to accomplish our goal: we ask _beginthread() to employ a static class member function (which. you sometimes run into difficulties.h> #include <windows. you will now see listed "Runtime Library". etc. This is one of the discrepancies from what Jaeschke says concerning C++/CLI.] You need to click on this entry to observe a drop-down list control.CodeProject http://www. but you need to know the key to starting a multi-threaded program from scratch: you must remember to perform one modification to the default project properties that the New Project Wizard gives you. 0.a. The death of this thread means the death of all the other threads. _beginthread( silly.aspx #include <stdio. with the passed argument -5. Using a C++ Member Function as the Thread's Entry-Point-Function The example program I just listed really isn't a C++ program because it doesn't use any classes. Simply request a Win32 Console Program from Visual C++ . Go ahead and compile this program. This defaults to "Single Threaded Debug (/MLd)". In the left hand column of this dialog. it is easy to forget they exist. lacks the hidden this parameter). where you must select Multi-threaded Debug (/MTd). Without the Sleep() statement. I am providing Visual C++ . The function signature required by _beginthread() does not allow the hidden this parameter. "Linker". If you forget to do this.com/KB/threads/MultithreadingTutorial. // Let's now create our second thread and ask it to start // in the silly() function. and hence a C++ member function cannot be directly activated by _beginthread(). [The notation /MLd indicates that this choice can be accomplished from the compiler command line using the /MLd switch. Sleep( 100 ). Via the this parameter. // From here on there are two separate threads executing // our one program. let me remind you of the problem. Voila! We now know which instance of the 3 of 8 8/14/2010 2:49 AM .NET 2003 workspaces for Jaeschke's (modified) programs. So. the program's output will probably only show a single call to the silly() function. The static class function knows to convert the void* parameter to a pointer of a class instance. // needed for _beginthread() // function prototype int main() { // Our program's first thread starts in the main() function.h> #include <process.CPP file) in which you place the statements I have shown. click on "Code Generation".Multithreading Tutorial . } void { } silly( void *arg ) printf( "The silly() function was passed %d\n". let's again consider the _beginthread() function which allows us to specify an arbitrary entrypoint-function for our new thread. with the main sub-nodes labeled "C/C++". the function knows which instance of the class to operate upon.h> void silly( void * ). Aye. Evidently. and the error message will complain about the _beginthread() identifier. you will see a tree view control named "Configuration Properties". We would be in a bind were it not for the fact that C and C++ are incredibly expressive languages (famously allowing you the freedom to shoot yourself in the foot) and the additional fact that _beginthread() does allow us to specify an arbitrary passed parameter to the entry-point-function. printf( "Now in the main() function. It is just a C language program. // This main thread can call the silly() function if it wants to. Then. and this may occur before the Operating System has had the opportunity to create the other thread for this process. Namely. Not so for straight C++ Win32 programs: the process dies when the primary thread (the one that started in the main function) dies. silly( (void*)-5 ). This is because the program's process terminates as soon as the main thread reaches the end of the main() function. In the right hand area of the Project Properties dialog. and we send this static class function the hidden this pointer as a void*.NET 2003's New Project Wizard and then "Add a New item" which is a C++ source file (. and when you employ it with C++ programs. and the overall process (which is the container for all the threads) persists until the last thread has decided to die. A very interesting thing happens if you comment out the call to the Sleep() function seen in this example program. unlike an instance function. Because you never see these this parameters. Every C++ member function has a hidden first passed parameter known as the this parameter. your program won't compile. (void*)12 ). you must open up the Project Properties dialog (select "Project" from the main Visual C++ menu and then select "Properties").\n" ). in C++/CLI. The Win32 API was really designed for the C language. each thread has an independent lifetime. Double-click on the "C/C++" node to open this entry up. Such as this difficulty: "How can I employ a class member function (a. there's the rub. Now.k. This entry-point-function must accept a single void* passed param.codeproject. an instance function) as the thread's entry-point-function?" If you are rusty on your C++.

the non-primary threads will never get a chance to run because the process will die when the primary thread reaches the end of the main() function. we get the two step process started as shown below: Collapse hth1 = (HANDLE)_beginthreadex( NULL. _beginthreadex() allows the entry-point-function to return an unsigned value. static unsigned __stdcall ThreadStaticEntryPoint(void * pThis) { ThreadX * pthX = (ThreadX*)pThis.Multithreading Tutorial . the multiple threads don't interact with one another. Synchronization Between Threads In the Part 1 Listing 1 program. you will see some statements which have no counterpart in Jaeschke's original program. // stack size ThreadX::ThreadStaticEntryPoint. // arg list holding the "this" pointer CREATE_SUSPENDED. and this call completes the two step process. the process terminates when the primary thread exits.aspx class should call the real entry-point-function. The key is to provide synchronization whenever shared data is accessed (either written or read). } } // the thread exit code Then. // or it can terminate itself with a call to _endthread(). and hence they cannot corrupt each other's data. We force the primary thread (the thread that starts in the main() function) to wait upon the other two threads. the process continues until the last thread exits. The relevant code (from Jaeschke's modified Part 1 Listing 1 program) is shown below: Collapse class ThreadX { public: // In C++ you must employ a free (C) function or a static // class member function as the thread entry-point-function. INFINITE ). more powerful augmented techniques were introduced. all its threads are then terminated. // the tricky cast pthX->ThreadEntryPoint(). // so we can later call ResumeThread() &uiThread1ID ). This is all demonstrated in the "Part 1 Listing 1" program I provide (the name comes from Jaeschke's magazine article). and when the process terminates. INFINITE ). WaitForSingleObject( hth2. in C++. which means this version offers additional capability not available with _beginthread(). One of these new extended capabilities is that the _beginthreadex() function allows me to create but not actually start my thread. // security 0. return 1. The synchronization objects provided by Win32 are: event mutex or critical section semaphore waitable timer 4 of 8 8/14/2010 2:49 AM .// entry-point-function o1. Furthermore. Notice that I am using _beginthreadex() rather than _beginthread() to create my thread. This type of corruption is very difficult to debug. This is typical of Microsoft's Win32 API: when shortcomings were identified. The point of the Part 1 Listing 2 program is to demonstrate how this corruption comes about.CodeProject http://www. However. in the main() function. } void ThreadEntryPoint() { // This is the desired entry-point-function but to get // here we have to use a 2 step procedure involving // the ThreadStaticEntryPoint() function. and this makes multi-threaded programs very time consuming if you don't design them correctly. The "ex" stands for "extended". That is. Hence. the threads have independent lifetimes. This is because in C++/CLI. At the end of the main() function.com/KB/threads/MultithreadingTutorial. via the following statements: Collapse WaitForSingleObject( hth1. I elect this choice merely so that my program better matches Jaeschke's C++/CLI code. A synchronization object is an object whose handle can be specified in one of the Win32 wait functions such as WaitForSingleObject(). Jaeschke's original code was designed to show that the primary thread could exit and not influence the other threads. // now call the true entry-point-function // A thread terminates automatically if it completes execution. If you comment out these waits.codeproject. and this is handy for reporting status back to the thread creator. The thread's creator can access this status by calling GetExitCodeThread().

which is used with a mutex. It now works perfectly. the Operating System will be fair to all threads. this is enough interaction that the program will display a bug if used without synchronization. if you simply compile and run the Part 2 Listing 1 program as I provide it. and an instance of the ProcessMessages class acts as the consumer.CodeProject http://www. This one change activates all of the critical section statements in the program. Since my Part 1 Listing 2 program demonstrates a critical section. remove the double slashes). Note that in the main() function.com/KB/threads/MultithreadingTutorial. the print out of x and y values will show a discrepancy between the x and y values. Only one thread at a time can own a mutex object. This happens because the thread that updates x and y was interrupted by the thread that displays the values between the moments when the x value was incremented and when the y value was incremented.aspx An event notifies one or more waiting threads that an event has occurred. until commanded to die. this program guarantees that the consumer thread can't process the contents of the message buffer until the producer thread has put something there. Via synchronization statements. I elected to employ a mutex in this Part 2 Listing 1 program. As with the Part 1 Listing 2 example program. The Producer/Consumer Paradigm One of the most common uses for a multi-threaded architecture is the familiar producer/consumer situation where there is one activity to create packets of stuff and another activity to receive and process those packets. Because in one case isMover = true and in the other case isMover = false. the two threads act completely different from each other. but the critical section is the most light-weight (hence fastest) synchronization object offered by Windows. EnterCriticalSection() waits indefinitely for ownership whereas WaitForSingleObject(). Take a look at the source code now. The state of a mutex object is set to signaled when it is not owned by any thread. Compile and run the program as I provide it to see the problem. The primary thread waits for the producer thread to die. one of the threads continually changes the Point object's x and y values while the other thread merely displays these values. Then. The producer creates exactly five messages and then commits suicide. we create two threads and ask them both to employ the same entry-point-function. because the two object instances (o1 and o2) have different values for the mover class data member. and that the producer thread can't put another message there until the previous one has been consumed. Critical section objects provide synchronization similar to that provided by mutex objects. limiting the number of threads that are simultaneously accessing a shared resource. Whereas the producer creates the five following messages: Collapse 1111111111 2222222222 3333333333 4444444444 5555555555 the consumer receives the five following messages: Collapse 5 of 8 8/14/2010 2:49 AM . a critical section object can be owned by only one thread at a time. re-compile and re-run the program. whose name comes from the fact that it is useful in coordinating mutually exclusive access to a shared resource. allows you to specify a timeout. An instance of the CreateMessages class acts as the producer. The program has a single instance of the MessageBuffer class. However. There is no guarantee about the order in which threads will obtain ownership of the critical section. and to nonsignaled when it is owned by a thread. Now.cpp file and find the following statement: Collapse //#define WITH_SYNCHRONIZATION Uncomment this statement (that is. A mutex can be owned by only one thread at a time. The next example program comes from Jaeschke's Part 2 Listing 1 program. This Part 1 Listing 2 program demonstrates the Critical Section synchronization object. Occasionally. Like a mutex object.Multithreading Tutorial . and then commands the consumer thread to die. go to the top of the Main. the x value will be 1 larger than the y value. The consumer is designed to live indefinitely. however.codeproject. and this one instance is shared by both the producer and the consumer threads. A semaphore maintains a count between zero and some maximum value. When this happens. namely the function called StartUp(). except that critical section objects can be used only by the threads of a single process (hence they are lighter weight than a mutex). I could have just as well used a mutex or a semaphore. But. enabling threads to coordinate mutually exclusive access to a shared resource. you will see that it has a bug. which makes it useful for protecting a shared resource from simultaneous access. Another difference between a mutex and a critical section is that if the critical section object is currently owned by another thread. A waitable timer notifies one or more waiting threads that a specified time has arrived.

CodeProject http://www. remove the double slashes). After you have seen the problem. The C++ keyword which declares that a variable should employ thread local storage is __declspec(thread).h header file: Collapse #define STATUS_PENDING ((DWORD )0x00000103L) Note that 0x00000103 = 259. Assembly language statements are naturally atomic: they cannot be interrupted half-way through.NET 2003 compiler rather than the Release mode settings. go to the top of the Main. such a bug can be demonstrated as long as we employ the Debug mode settings in the Visual C++ . I said that an Operating System could initiate a new thread faster than it could initiate a new process because all threads share the same memory space (including the heap) and hence there is less that the Operating System needs to set up when creating a new thread. At the start of this article. this one will display an obvious synchronization problem if you compile and run it unchanged. After you have seen the problem. This is not true of high-level C or C++ statements.cpp file and 6 of 8 8/14/2010 2:49 AM . Now. You can find the definition for this value in the WinBase header file: Collapse #define STILL_ACTIVE STATUS_PENDING where you can find STATUS_PENDING defined in the WinNT. But. But. Windows XP does not support InterlockedIncrement64(). go to the top of the Main. this one will display an obvious synchronization problem if you compile and run it unchanged.Multithreading Tutorial . It now works perfectly. which is the situation where an operation will fail if it is interrupted mid-way through. you should be able to follow the flow. Between the English explanation in Jaeschke's original magazine article and all the comments I have put in my C++ source code.com/KB/threads/MultithreadingTutorial. Thread local storage is memory that is accessible only to a single thread.cpp file and find the following statement: Collapse //#define WITH_SYNCHRONIZATION Uncomment this statement (that is. I was originally worried that I wouldn't be able to demonstrate an atomicity bug in a Windows XP program that dealt only with 32 bit integers. you will notice that unlike the other example programs inside the . When you request thread local storage.aspx 1 2111111111 3222222222 4333333333 5444444444 There is clearly a synchronization problem: the consumer is getting access to the message buffer as soon as the producer has updated the first character of the new message. Hence. go to the top of the Main. Whereas you might consider an update to a 64 bit variable to be an atomic operation.ZIP file that I distribute. But the rest of the message buffer has not yet been updated. But. remove the double slashes). it actually isn't on 32 bit hardware. this one is set for a Debug configuration. This example program could be rewritten to employ 64 bit integers (the LONGLONG data type) and the InterlockedIncrement64() function if it only needed to run under a Windows 2003 Server. re-compile and re-run the program. Microsoft's Win32 API offers the InterlockedIncrement() function as the solution for this type of atomicity problem. you are asking the Operating System to erect a wall around certain memory locations in order that only a single one of the threads may access that memory. curiously. Therefore. Atomicity Jaeschke's Part 2 Listing 4 program demonstrates the problem of atomicity. re-compile and re-run the program. As with my other example programs. here is the exception to that rule. The final comment I will make is that the GetExitCodeThread() function returns the special value 259 when the thread is still alive (and hence hasn't really exited).cpp file and find the following statement: Collapse //#define WITH_SYNCHRONIZATION Uncomment this statement (that is. alas. Then. As with my other example programs. Thread Local Storage Jaeschke's Part 2 Listing 3 program demonstrates thread local storage. It now works perfectly.codeproject. Then. This usage of the word "atomic" relates back to the time when an atom was believed to be the smallest particle of matter and hence something that couldn't be further split.

My home page is www. License This article.ZIP file holding five Visual C++ . I also have a website where I offer a complete introduction to programming. Part 2 Listing 3. Part 1 Listing 2. Agrawal sylgas First Prev Next 8:28 10 Aug '10 23:33 10 Sep '09 11:36 22 Mar '09 21:35 17 Jan '09 7:00 8 Jan '09 0:13 2 Jul '08 20:13 23 Mar '08 0:59 16 Nov '07 16:19 30 Oct '07 21:53 2 Apr '07 19:52 17 May '07 0:15 2 Apr '07 9:10 6 Feb '07 7 of 8 8/14/2010 2:49 AM . and Part 2 Listing 4 programs from Jaeschke's original article (now translated to C++). The first demonstrated how to use Direct3D 8 to model the Munsell color solid so that you could then fly through this color cube as in a video game. It now works perfectly because it is now employing InterlockedIncrement().com/KB/threads/MultithreadingTutorial. // change this to fix the problem Change false to true.codeproject. and then re-compile and re-run the program. result of Part2Listing3 Executing Two Functions Simultaneously multiple parameters in arglist? Medium Search Layout Normal Per page 25 Update Msgs 1 to 25 of 30 (Total in Forum: 30) (Refresh) Olaf Petersen srivas amirkool CNN73 aryamunish RTrelles Alexei Valyaev ben_th yesitookmypills vic12000 PhilDeets Ms. along with any associated source code and files. // No success multithreading with WinMain // join Thanks for this great Tutorial ! Great Article the purpose of the event in Part2Listing1. Enjoy! Conclusion This is my second submission to CodeProject.NET 2003 workspaces for the Part 1 Listing 1. including assembly language programming.CodeProject http://www.computersciencelab. is licensed under The Code Project Open License (CPOL) About the Author John Kopplin United States Member Article Top Rate this article for us! Poor Excellent Vot e Comments and Discussions FAQ Noise Tolerance New Message My vote of 5 A possible flaw hw t convert c# file to VLSI chip problem with the Part 2 Listing 3 program ReadArticleAndRunCode-YouWillBeAmazedWithExcellencyOfArticle. The Example Programs In order that other C++ programmers can experiment with these multithreaded examples. result of Part2Listing3 Re: the purpose of the event in Part2Listing1.Multithreading Tutorial . I make available a .aspx find the following statement: Collapse static bool interlocked = false. Part 2 Listing 1.com.

1999-2010 Web20 | Advertise on the Code Project 8 of 8 8/14/2010 2:49 AM . Ctrl+Up/Down to switch threads. PermaLink | Privacy | Terms of Use Last Updated: 28 Dec 2006 Copyright 2006 by John Kopplin Everything else Copyright © CodeProject.Multithreading Tutorial .com/KB/threads/MultithreadingTutorial. Ctrl+PgUp/PgDown to switch pages.codeproject.aspx Re: multiple parameters in arglist? Great! No obvious synchronization problem Article Formatting Re: Article Formatting Duo core aware? Thanks Event Description is missing [modified] Thanks C++ 2005 Cache miss? Re: Cache miss? Last Visit: 8:28 13 Aug '10 General News Last Update: 14:07 13 Aug '10 Question Answer Joke Rant gc3sanjose Kaixi Luo Michel Wassink Jeffrey Walton John Kopplin Nguyen Luong Son WhiteSky prasikumbhare ssanand DotNET42 Thief^ John Kopplin 12:16 27 Jun '07 12:19 4 Jan '07 4:17 4 Jan '07 0:21 29 Dec '06 10:45 29 Dec '06 1:38 30 Sep '06 23:53 15 Aug '06 5:21 21 Jul '06 6:26 18 Jul '06 6:15 18 Jul '06 1:04 18 Jul '06 21:33 19 Jul '06 1 2 Next » Admin Use Ctrl+Left/Right to switch messages.CodeProject http://www.

Sign up to vote on this title
UsefulNot useful