1 / 42

Multithreading in C++

Multithreading in C++. CS204 Advanced Programming Sabancı University not in the books , try to understand in class / labs there are good explanations at http://www.cplusplus.com/reference/multithreading/. Overview. Terminology Process es Thread s Multithread ing

rcurtiss
Download Presentation

Multithreading in C++

An Image/Link below is provided (as is) to download presentation Download Policy: Content on the Website is provided to you AS IS for your information and personal use and may not be sold / licensed / shared on other websites without getting consent from its author. Content is provided to you AS IS for your information and personal use only. Download presentation by click this link. While downloading, if for some reason you are not able to download a presentation, the publisher may have deleted the file from their server. During download, if you can't get a presentation, the file might be deleted by the publisher.

E N D

Presentation Transcript


  1. Multithreading in C++ CS204 AdvancedProgramming Sabancı University not in thebooks, trytounderstand in class/labs therearegoodexplanations at http://www.cplusplus.com/reference/multithreading/

  2. Overview • Terminology • Processes • Threads • Multithreading • Threadsynchronizationandsynchronizationconflicts • How toresolvethemusingmutex • Multithreading in C++ (standart classes/functions)

  3. Processes • What is a"process"? • For the sake of simplicity, let us define that a processis a running program/application in a computersystem. • But sometimes, a program may have more than one process. • But always, each application has at least one process. • A processhas self-contained execution environment. • it has own code, data, address space in the memory, and some other resources (some of themareto be sharedwithotherprocesses). • The Operating System (OS), such as MS Windows, LINUX, Android, iOS, etc. facilitates sharing of computer resources by many processes concurrently. • To give the illusion that many processes are executing concurrently, the CPU switches between processesfastly: this is calledcontextswitching • Theprocessescan work independentlyorbycooperatingandcommunicatingwith other processes • Cooperation/communicationamongprocesses is not in CS204 scope

  4. Threads • Actuallytheprocessor (CPU) does not executeprocesses, but executesthreads • A process is composed of oneormore threads • A threadis a "path of execution" in a process • it is a single sequential flow of control within a process • Theresources of theprocessaresharedamongits threads (includingmemoryandfiles) • Although a particularthreadmayhaveitsownvariables/objectsandotherresources • As you can see, threads aresimilartoprocesses • But threads aremoreflexibletousecooperatively since theysharememoryandresourceswithin a process/program • Inallprogramminglanguages, there is supportto program with threads andstartingtodaywewillsee how to do thisusingstandard C++ techniques. • Several threads of a process/program workconcurrently • Theyseemstowork in parallel, but actuallythere is contextswitching • Everyprocess has at leastonethread (calledthemainthread) • Thatwastheonlycase in CS201/CS204 so far (having a single main thread).

  5. Processes & Threads

  6. TheStorySo Far Youcompile, buildyourcode .exe You runyourexecutable Yourprocessstarts Theinstructionsareexecutedsequentially Themainthread is located Instructionsfinish, themainthreadreturnsandyourprocessends

  7. Now! Multithreading • You stillhavethemainthread, but therearegoingto be someother threads workingconcurrently in thesameprocess • Whymultithreading? • Theanswer is alreadygiven. Weneedconcurrencyandmultitaskingin somecases. • A typicalapplication has manytasks • Therealcomputationand data processing • Sometimesseveralobjectsworking on thesamesharedresource in parallel. • Interactingwiththeuservia a GraphicalUserInterface (GUI) • Input/Output (I/O) operations • etc. • Allthesetasksarebetterto be handledconcurrently • Wouldn't it be greatifthecomputationcontinueswhilewaitingforinputfromuser?

  8. Multithreading: a simpleexample • Starting from themain thread you can spawn "child" threads • Child thread : The spawned thread • Parent thread: the thread that spawned compute thread compute I/O thread I/O I/O request I/O complete I/O result Needed I/O result Needed compute (a) Sequential process (b) Multithreaded process

  9. Multithreaded Programming • No set standard in C++ for multithreaded programminguntilC++11 (11th version of the C++ standards) • Every library or framework implements threads in a different way • Pthreads for *NIX • Windows Threads • Higher Level Libraries: • Boost:thread • Intel’s Thread Building blocks • OpenMP • Ting : ThreadING (Multiplatfrom library win/linux/OSX/Android) • Every OS has a different thread implementation. • In CS204, we will usestandard C++ threads

  10. Multithreading in C++ • threadobject is usedbytheC++ standardlibrarytomakethethread-management taskseasier • launching threads • checkingiftheyare finished • keeping an eye on them • Thelibrary has manyfunctionalities: wewillonlycover a few of thesefunctionalities in thiscourse. • Every C++ program has at least one thread, • a thread running main() started by the C++ runtime. • Wewillcall it themainthread • You do not needto do somethingspecialtorunthemainthread; whenyourunthe program it automaticallystarts • main threadcan spawnadditional threads that have another function as the entry point • Entrypointis thestartingfunction of thethread • allthesethreads run concurrently with eachother • via a scheduleorganizedbythe OS; theprogrammercannotdirectlycontrolthisschedule

  11. A simplemultithreadedexample standard C++ headertouse threads #include<iostream> #include<thread> usingnamespace std; void hello() { cout << "Hello thread\n"; } int main() { threadaThread(&hello); aThread.join(); cout << "Bye main\n"; return 0; } Later, thisfunctionwill be theentrypoint (startingpoint) ofaThread Themainthreadstartshere aThreadstarts (is spawned) here. Parentthread: main Child thread:aThread Everythread has tohave an initialfunction (entrypoint) wherethenewthread of executionbegins. The new thread is started by constructing aThread object that specifies the task hello() to run on that thread. Hello thread Bye main See threads1.cpp

  12. Anothermultithreadingexample #include<iostream> #include<thread> usingnamespacestd; voidhello() { inti; for(i=0; i<100; i++) cout<< "Hellothread "; } int main() { threadaThread(&hello); inti; for(i=0; i<100; i++) cout<< "Hi main "; aThread.join(); cout<< "Bye main\n"; return0; } • The previous example may look like a regular function calling. However, this is not the fact. Both main and aThreadthreads work concurrently. • To visualize concurrency, consider the example on the left, where several outputs are displayed on each thread. • Please run this several times. You will see that "Hello thread" and "Hi main" strings wouldreallyinterleave. • See threads2.cpp

  13. Some syntax • The thread objects can be created with or without initialization • Execution of thread starts once the thread object is initialized • Below is the syntax for creating with initialization (usingparametricconstructor) threadthread_object_name(&starting_function,argument list); • Below is the syntax for creating the thread object without initialization (usingdefaultconstructor). Thethreaddoes not start. threadthread_object_name; • Below is the syntax for initialization of an alreadycreatedthread object. Thethreadnowstarts. thread_object_name= thread(&starting_function,argument list); See threads3.cpp foranotherexamplewith arguments

  14. join • Once a thread, say aThread, is started by the main thread, normally the main thread should wait for aThread to finish before it terminates. • Otherwise, the resources of the main (parent) thread becomes destroyed but the child thread (aThread) still wants to use them. This causes your program to crash. • Thus, the parent threads cannot be terminated before the childthreads are finished. • You make sure that parent thread does not terminate before the child threads finish using the thread method join() on child threads (in our case aThread) within the parent thread function (in our case main). Syntax: Example: thread_object_name.join();athread.join();

  15. More on join • Actually, join() causes the current thread in which it was called (in our case the main thread) to be blocked until the thread on which it was called (in our case aThread) completes and terminates. • That is why in the previous examples, "Bye main" was displayed always at the end. • You can use this characteristicof join() for the cases the parent thread needs the value generated by the child thread to continue (for example, when the child thread reads data and parent thread needs it). • Of course, iftheexecutionaThread had alreadycompletedbefore, thecurrentthreadcontinueswithoutwaiting void hello() { cout << "Hello thread\n"; } int main() { threadaThread(&hello); aThread.join(); cout << "Bye main\n"; return 0; }

  16. More on join Ifjoin is not calledthen, themainthreaddoes not waitforaThreadtoterminate. It is not safe since aThread is stillusingsomeresources. Thusyour program crashes. Sometimes it crashesafteralloutput is displayed, but thiscase is still a problem. void hello() { cout << "Hello thread\n"; } int main() { threadaThread(&hello); // aThread.join(); cout << "Bye main\n"; return 0; }

  17. More on join • In order to call join() on a thread object, that thread must be joinable. • A thread object is said to be joinable: • if it is not the current thread (i.e. you cannot call join on a thread inside itself) – well, you have to work hard to be able to do so . • if it has not joined/detached before • if it is not a default constructed thread object which has not yet been initialized (e.g. thread aThread; ) • If you call join() on a thread which is not joinable, then your program crashes. • We can check the thread if it is joinable before joining to avoid these problems if (aThread.joinable()) aThread.join(); • Be careful!!! MS VS does not check the first joinable rule (cannotcalljoin in itself) bythejoinable()function (this is againstthestandardthough); however, ifyoutrytojoin on a thread inside itself, your program crashes. • Let us see some of the cases that cause crash and use of joinable() in threads4.cpp

  18. freedomfor a thread - detach • Sometimes (but not often) wewantthechild threads tobecomeindependent of parentone. • Forexample, whenthechildthread is performing a longtaskand main does not needanythingfromchild. • Thedetach()function of threadclass detaches the thread on which it is called from the calling (parent) thread, allowing them to execute independently from each other. • Aftercallingdetach(), both threads continue torun in parallel. Note that when either one ends execution, its resources are released. • After a call to detach(), thethreadobject becomes non-joinable. • Afterthe main threadfinishes, anyincompletedetached threads continuetheirexecution in the background. • So you may not seesome of theoutputs of thedetached threads on screen (theonesthatwouldhavebeendisplayedafter main ends). • Thus you may say thatdetach()is not souseful, and I agree. Let us see threads5.cpp for an example of detach() use.

  19. this_thread • This namespace containsa set of functions that access the current thread. • Functions • get_id – Returnsthethread id. No parameters • yield - Yields to other threads (i.e. Itdoes not workforthatscheduledduration). No parameters. • Kind of advancedissue, willskip. • sleep_until - Sleep until a time point given as parameter • sleep_for - Sleep for a givendurationgiven as parameter • Usage this_thread::function_name(arguments_if_any);

  20. this_thread::get_id voidhello(intorder) { cout << order << " "<<this_thread::get_id() << endl; } int main() { int i; threadthreads[5]; for (i=0; i < 5; i++) { threads[i] = (thread(&hello, i)); } for (i=0; i < 5; i++) { threads[i].join(); } return 0; } Returns the thread id of the calling thread.This value uniquely identifies the thread. Outputs of theexecutions 0 85203 2 2676 4 5284 1 2480 7360 0 5316 1 3 2936 3616 2 9212 4 2952 • Same program, but differentexecutions. Onereason is thatthread_idsaredifferent at each program run. Otherreason is multithreadinganddifference in scheduling • Thereasonthattheoutputsaremixedup is interleavedexecution of threads evenduringonecoutstatement. See threads6.cpp

  21. this_thread::sleep_for #include<iostream> #include<thread> #include<chrono>// fortiming usingnamespacestd; voidpause_thread(intn) { this_thread::sleep_for (chrono::seconds(2*n)); cout << "thread " << n << " is here\n"; } int main() { cout << "Spawning 2 threads...\n"; thread t1 (pause_thread,1); thread t2 (pause_thread,2); cout << "Done spawning threads.\n"; cout << "Now waiting for them to join:\n"; t1.join(); t2.join(); cout << "Bye from main!\n"; return 0; } means 2*n seconds. Wewillseemoreaboutchrononamespacelater. You can useminutesforlonger, millisecondsandmicrosecondsforshorterdurations. Spawning 2 threads... Done spawning threads. Now waiting for them to join: thread 1 is here thread 2 is here Bye from main! • Here wealwaysseethesameoutput. This is duetodifferentwaitingtimes in threads. Inthisway, one of themreturnswhiletheother is stillwaiting. • Trythesamewaiting time andseethattheoutputagainbecomesgarbled. See threads7.cpp

  22. this_thread::sleep_until (abs_time) • Blocks the calling thread until abs_time. • other threads may continue to work. • abs_time is of a data type from chrono namespace of C++. • No need to understand the details of time structures, but you should be able to apply the basics by using analogy from the examples below. • Example: race of 10 threads • All threads will start at the same time (beginning of the next minute). • In the thread function (count1m), an empty loop will iterate 1 million times. Actually this is the race (counting to 1 million). • With sleep_until, the order of threads finishing the loop looks random (try several runstoseethis) • Without sleep_until, the threads starting first (0, 1, 2, ..) are more likely to finish first (try several runs by commenting out sleep_until. • See the code in the next slide and in threads8.cpp

  23. this_thread::sleep_until (abs_time) ptm is the name of the parameter. The others are fixed code #include<iostream> #include<iomanip>// forput_time #include<thread> #include<chrono>// forchrono::system_clock #include<ctime>// for time_t, tm, localtime, mktime usingnamespacestd; void count1m(intid, structtm *ptm) { this_thread::sleep_until(chrono::system_clock::from_time_t(mktime(ptm))); for (int i=0; i < 1000000; ++i) {} cout << id; } Comment out sleep_until line to see the pattern of thread completion order is similar to the creating order. int main () { time_ttt = chrono::system_clock::to_time_t (chrono::system_clock::now()); structtm *ptm = newstructtm; localtime_s(ptm, &tt); cout << "Time is now " << put_time(ptm,"%X")<<endl; ptm->tm_min++; //new time is thenextmin ptm->tm_sec=0; cout<< "Race will start at " << put_time(ptm,"%X") << endl; threadthreads[10]; for (int i=0; i<10; i++) threads[i] = thread(count1m, i, ptm); for (int i=0; i<10; i++) threads[i].join(); cout << endl; return 0; } gets the current time creating the time struct to be used in thread converting the time structures Time is now 11:29:17 Race will start at 11:30:00 6574392180

  24. Re-initialization of a thread Object • An existing thread object can be re-initiliazed to run as another thread after join() or detach() • To do so, you just assign thread(&func_name, arguments) to the thread object. • If you reassign/reinitialize a thread object without join() or detach(), then your program crashes. • In this way, you can have multiple threads in your program using a single thread object. • If you use join() between reassignment, threads work consecutively (i.e. one ends and the other starts) • If you use detach() between reassignments, several threads may work concurrently • Moreover, if you usedetach()you may not seealltheoutputs of thechild threads(since the main threadmayendbeforethechild threads). As a remedy, you maysleepthe main threadfor a while at theend. See threads9.cpp for an examplecode

  25. Sharingamong threads • Inallexamples, threads wereworkingconcurrently • Whatiftwoormore threads modifiesthesamevariable? • Let us see in an example • You can neverknowtheanswer. • Itrarelybecomestheexpectedvalue of 2000000. • Mostlyit is below2000000. • Thereason is at schedulingthe threads in themiddle of theincrement. • Wewillseemore on this problem andsolutions. But beforethatwewillseesomebasics. Declare a global value = 0 ThreadFunctionincrements global value 1000000 times Thread 1 Thread 2 What is the final value of valueaftertheyjoin in main?

  26. Sharingamong threads: Scheduling • Scheduling is a key concept in computer multitasking and multiprocessing operating system designs • Scheduling refers to the mechanismthatprocesses are assigned to run on the available CPUs, since there are typically many more processes running than number of available CPUs. • This assignment is carried out by software known as a scheduler and dispatcher.

  27. Sharingamong threads: Scheduling • Example: Three threads sharing a single CPU • Actuallythis is a simplifiedand ideal scheduling. Normallyschedulingmay not be round-robinandallocateddurationsmayvary • Facts about scheduling! • Multiple threads share the same process resources • Scheduler schedules them according to "some" algorithmthatonly OS knows (i.e. you cannotcontrolscheduling) • The sequence of scheduling can create conflictsandproblems • Read-write conflicts (not I/O type of read-write; wemeanreadingandwritingfrom/tomemory) • Deadlocks • In general, race conditions

  28. Sharingamong threads: SynchronizationConflictsLet us gobacktoincrement problem Global valueto be sharedby threads intvalue = 0; #defineTHREADS_NUM 2 voidincrement() { for(int i=0; i <1000000; i++) value++; } int main() { inti; cout<< "At the beginning of main value is: " << value << endl; threadthreads[THREADS_NUM]; for(i=0; i < THREADS_NUM; i++) threads[i] = thread(&increment); for(i=0; i < THREADS_NUM; i++) threads[i].join(); cout<< "At the end of main value is: " << value << endl; return0; } Eachthreadincrementsvalue 1000000 times The final value of valuedoes not reach 2000000 formost of the time (it mayrarely be 2000000). Let us seethisbyrunning threads10.cpp. Thereason of thisunexpectedbehavior is explained in thenextslide.

  29. SynchronizationConflict: WhyHappens? • value++has threeinternalsteps (we do not seethis in program, but internallythesehappen). • First thevalue of value is readfromthememorybythethread • Thethreadincrements it. • Thethreadwritesthenewvalue of valuebacktomemory. • Schedulingmayhappen in themiddle of thesethreesteps. • Example scenarios (switchingoccurs in thearrowpoints): Thread 1 reads value • Thread 2 reads value • Thread2 increments • Thread 2 writes to value • Thread 1 increments • Thread1 writes to value Thread 1 reads value Thread 1 increments • Thread 1writestovalue • Thread 2readsvalue • Thread2 increments • Thread 2 writesto value Thisyieldswrongvalue since both threads incrementthesamevalueandvalue is incrementedonlyonce, althoughwethinkthat it is incrementedtwice This is correct since value is incrementedtwice

  30. SynchronizationConflict: WhyHappens? • The problem explained in thepreviousslidemayhappenseveraltimes in theexamplecode (threads10.cpp). • This problem is a read-writetype of synchronizationconflict • Theobject is readbyonethreadfrommemoryandbeforehaving a chancetowritetheupdatedvalue, anotherthreadreads it. • Inorder not toallowthis, read-process-writeoperationsmust be done withoutswitchingtoanotherthread. • Read-write conflict is an example of the general concept of race condition • Order of execution of the threads in a critical section of a code makes the result wrong • As saidbefore, theswitchingmayoccurany time duringschedulingandwecannotcontrolscheduling. • Thus, wehavetohaveothermechanismstoavoidread-writeconflicts. Fortunately, therearewaystohandletheseproblems as will be seenlater.

  31. SynchronizationConflicts: AnotherExample • Typicalexample: Producer-Consumer Problem • Sharedqueue of items • Producer thread(s) additemstothequeue • Consumer thread(s) removeitemsfromthequeue IntQueuedataQ(); void producer() { if(!dataQ.isFull()) { Data d = ProduceData(); dataQ.enqueue(d); } } void consumer() { if(!dataQ.isEmpty()){ Data d = dataQ.dequeue(); ConsumeData(d); } } • Supposethereare3 threads: • thread1: Produces data and puts it into a queue • thread2: Checks the queue and if there is a data removes and consumes it • thread3: Checks the queue and if there is a data removes and consumes it • How can things go wrong in this producer-consumerexample?

  32. Synchronization Conflicts • Problematic scheduling : • Supposethat onlyone data left in queue • thread2(consumer) checks for empty and receives false and enters the if-statement • thread3(consumer) checks for empty and receives false and enters the if-statement • thread3dequeues and consumes • thread2 tries to dequeue an empty queue and crashesorgiveserror. • Whatifthequeue is size-limitedandwehavetwoproducers? • A similarscheduling problem mayoccurifthere is onlyoneempty spot andeachproducerthreadchecksisFull()beforetheotherenqueues.

  33. SynchronizationConflicts • There are several solutions to remedythesynchronizationconflicts/ race conditions • Semaphores • Atomic references • Monitors • Condition variables • Compare and swap • etc. • Inthiscourse, we will see a specificsemaphorecalled"mutex" • This is themost general solution

  34. Mutex • A mutex (short for mutual exclusion) is a way of communicationamong threads or processes that are executing concurrently. • This communication is usually used to coordinate the activities of multiple threads or processes, typically by controlling access to a shared resource (or a critical section of the code) by "locking" and "unlocking" the mutex.

  35. Mutex • Actuallya mutex can be thought as a binary counter • To lock a mutex you "up" its value • To unlock/releasea mutex you "down" its value • Once a mutex is locked, only unlock operation is allowed on it. • Ifanotherthreadwantstolockthesame mutex, it shouldwaituntil it is unlockedbythelockingthread. • However, ifthelockingthreadtriestolockthesame mutex againbeforeunlocking it, thenthe program crashes/behavesunexpectedly. • If an unlocked mutex is (somehow) unlockedagain, thisgenerallycauses a crashorunexpectedresults. Thus you havetomanagethelock/unlocksequencescarefully. • Generally, mutex objectsarecreated as global to be sharedbymanythreadfunctions (whencreated, initially in unlockedstate). • However, onlythelockingthread can unlock it later. • If a mutex is in unlockedstate, thenanythread can lock it (sameordifferent threads). • A mutex can be lockedandunlockedseveraltimesbyseveral threads. • Letssee how do weuse mutex in C++in thenextslideand in threads11.cpp

  36. SolvingSynchronizationConflictsusingmutexRevisitingincrement problem A newheader file for mutex use . . . #include<mutex> mutexmyMutex; intvalue = 0; #defineTHREADS_NUM2 voidincrement() { for(int i=0; i <1000000; i++) { myMutex.lock(); value++; myMutex.unlock(); } } int main() { inti; cout<< "At the beginning of main value is: " << value << endl; threadthreads[THREADS_NUM]; for(i=0; i < THREADS_NUM; i++) threads[i] = thread(&increment); for(i=0; i < THREADS_NUM; i++) threads[i].join(); cout<< "At the end of main value is: " << value << endl; return0; } Global mutex objectcreated mutex is lockedbeforevalue++; sothatnootherthread can attempttoincrementvalue mutex is unlockedaftervalue++; sothatother threads can incrementvalue The final value of valuenowreaches 2000000 since at a given time the ++ operation on value can be executedbyonlyonethread. Let us seethisbyrunning threads11.cpp. Alsosee threads11.cpp forsomespecialcasesrelatedwithlock/unlocksequences

  37. SolvingSynchronizationConflictsusingmutexRevisitingproducer-consumer problem: just a sketch here; more in labsQuestion: wheretolock/unlockmyMutex? void consumer() {if(!dataQ.isEmpty()) {Data d = dataQ.dequeue(); ConsumeData(d); } } IntQueuedataQ(); mutexmyMutex; void producer() {if(!dataQ.isFull()) {Data d = ProduceData(); dataQ.enqueue(d); } } void consumer() {myMutex.lock(); if(!dataQ.isEmpty()) {Data d = dataQ.dequeue(); myMutex.unlock(); ConsumeData(d); } else myMutex.unlock(); } void producer() {myMutex.lock(); if(!dataQ.isFull()) {Data d = ProduceData(); dataQ.enqueue(d); } myMutex.unlock(); } locking/unlockingtoencapsulateonlyenqueueanddequeue is not a correctsolution since two threads can concurrentlycheckemptiness/fullness, whichyieldswrongresults.

  38. SolvingOutputTidiness Problem usingmutex • Insome of thepreviousexamplestheoutputs of the threads were not tidy. • Thereasonwasthreadscheduling in themiddle of cout. • Wesomehowsolved it usingostringstream but there is noguaranteethat a string will be displayed ot once. • A guaranteedwaytohave a tidyoutput is touse a mutex beforeandaftercout • Seethecodebelowand threads13.cpp voidhello(intorder) { coutMutex.lock(); cout<< order << " " << this_thread::get_id() << endl; coutMutex.unlock(); } try with and without lock/unlock to see the difference in the output format

  39. Non-blockinglocktrial: try_lock() voidincrement() { intunsuccessfulLock = 0; for(int i=0; i <10000; i++) { boolisLocked; isLocked= myMutex.try_lock(); if(isLocked) { value++; myMutex.unlock(); } else { unsuccessfulLock++; } } cout << "Unsuccessful Lock: " << unsuccessfulLock << endl; } • Normally, a thread, which is tryingtolock a mutex, waitsidleif it has beenlockedbyanotherthread. • Sometimes, you want a thread not to remainidlewhilewaitingfor a mutex. • but do something else when waiting for it. • There is a memberfunction of mutex forthispurpose: try_lock() • Ifthethreadsuccessfullylocks mutex, thentry_lock()returnstrue; otherwise, returnsfalse. • We can checkthisreturnedvaluetogetintothecriticalsectionordoingsomething else See threads12.cpp forthefullsamplecode

  40. TheRemaining Problem: Deadlock • A catastrophiccasethatoccurswhen a thread, say thread1, waitsfor a mutexto be unlocked, whichwillnever be unlocked • Onereason is thatmutex has beenlockedbyanotherthreadandthecorrespondingunlock has beenforgotten • Solution: do not forgettounlock! • Anotherreason is thatthethreadthat has lockedmutex, say thread2,has beenblocked since it is waitingforanother mutex to be unlockedandthisother mutex has beenlockedbythread1. • Solution: if you areusingmultiplemutexes, lockthem in thesameorder in allthreadfunctions. • Let us seethis problem belowandsolution in threads14.cpp void thread_func1() { myMutex1.lock(); myMutex2.lock(); myMutex2.unlock(); myMutex1.unlock(); } voidthread_func2() { myMutex2.lock(); myMutex1.lock(); myMutex1.unlock(); myMutex2.unlock(); }

  41. Passingreferenceparametersto threads • Since thefunctionsassociatedwith threads are not directlycalledwhenthethread is initialized, actually a copy is createdevenifthecorrespondingparameter is referenceparameter. • Thustheupdates in function is not reflectedbacktoparentthread. This is a problem. • Solution is touseref(argument)instead of justargumentwhileinitiliazingthethread. int main() { intcounter = 0; threadmyThread(&increment,counter); myThread.join(); cout<< counter << endl; return 0; } voidincrement(int & value) { for(int i=0; i <1000000; i++) { myMutex.lock(); value++; myMutex.unlock(); } } Output is unexpectedly 0. Solution is tochangethethreadline as threadmyThread(&increment,ref(counter)); Afterthat, outputbecomes 1000000. See threads15.cpp forthefullsample code

  42. CreatingRandomNumbersviaMultipleThreads • OurfamousRandGenclassusesstandardrand()functiontogeneraterandomnumbersequences • rand() is not a re-entrantandthread-safefunction • Technically more complex, but can be simply defined as having/manipulating static and/or global data • Effect of this in RandGenclass in a multithreaded program, threadsgeneratethesamesequence. (see threads16.ppt) • Luckily, wealsohavethread-saferandomnumbergenerators. Usethefollowingfunctioninstead of RandGen in multithreadedprogramswith #include <random> at thebeginning of the program. (seethreads16.cpp) intrandom_range(constint & min, constint & max) { staticmt19937generator(time(0)); uniform_int_distribution<int> distribution(min,max); returndistribution(generator); }

More Related