mastering c# concurrency - ebooksworld...a print book customer, you are entitled to a discount on...
TRANSCRIPT
![Page 1: Mastering C# Concurrency - EBooksWorld...a print book customer, you are entitled to a discount on the eBook copy. Get in touch with us at for more details](https://reader035.vdocument.in/reader035/viewer/2022062917/5ed62e6504e9cb4adb670aed/html5/thumbnails/1.jpg)
www.EBooksWorld.ir
![Page 2: Mastering C# Concurrency - EBooksWorld...a print book customer, you are entitled to a discount on the eBook copy. Get in touch with us at for more details](https://reader035.vdocument.in/reader035/viewer/2022062917/5ed62e6504e9cb4adb670aed/html5/thumbnails/2.jpg)
www.EBooksWorld.ir
![Page 3: Mastering C# Concurrency - EBooksWorld...a print book customer, you are entitled to a discount on the eBook copy. Get in touch with us at for more details](https://reader035.vdocument.in/reader035/viewer/2022062917/5ed62e6504e9cb4adb670aed/html5/thumbnails/3.jpg)
MasteringC#Concurrency
www.EBooksWorld.ir
![Page 4: Mastering C# Concurrency - EBooksWorld...a print book customer, you are entitled to a discount on the eBook copy. Get in touch with us at for more details](https://reader035.vdocument.in/reader035/viewer/2022062917/5ed62e6504e9cb4adb670aed/html5/thumbnails/4.jpg)
TableofContents
MasteringC#Concurrency
Credits
AbouttheAuthors
AbouttheReviewers
www.PacktPub.com
Supportfiles,eBooks,discountoffers,andmore
Whysubscribe?
FreeaccessforPacktaccountholders
InstantupdatesonnewPacktbooks
Preface
Whatthisbookcovers
Whatyouneedforthisbook
Whothisbookisfor
Conventions
Readerfeedback
Customersupport
Downloadingtheexamplecode
Errata
Piracy
Questions
1.TraditionalConcurrency
What’stheproblem?
Usinglocks
Lockstatement
Monitorclass
Reader-writerlock
Spinlock
Thread.SpinWait
System.Threading.SpinWait
System.Threading.SpinLock
www.EBooksWorld.ir
![Page 5: Mastering C# Concurrency - EBooksWorld...a print book customer, you are entitled to a discount on the eBook copy. Get in touch with us at for more details](https://reader035.vdocument.in/reader035/viewer/2022062917/5ed62e6504e9cb4adb670aed/html5/thumbnails/5.jpg)
Optimizationstrategy
Locklocalization
Shareddataminimization
Summary
2.Lock-FreeConcurrency
Memorymodelandcompileroptimizations
TheSystem.Threading.Interlockedclass
Interlockedinternals
Writinglock-freecode
TheABAproblem
Thelock-freestack
Thelock-freequeue
Summary
3.UnderstandingParallelismGranularity
Thenumberofthreads
Usingthethreadpool
Understandinggranularity
Choosingthecoarse-grainedorfine-grainedapproach
Summary
4.TaskParallelLibraryinDepth
Taskcomposition
Taskshierarchy
Awaitingtaskcompletion
Taskcancellation
Checkingaflag
Throwinganexception
UsingOSwaitobjectswithWaitHandle
Cancellationusingcallbacks
Latencyandthecoarse-grainedapproachwithTPL
Exceptionhandling
UsingtheParallelclass
Parallel.Invoke
www.EBooksWorld.ir
![Page 6: Mastering C# Concurrency - EBooksWorld...a print book customer, you are entitled to a discount on the eBook copy. Get in touch with us at for more details](https://reader035.vdocument.in/reader035/viewer/2022062917/5ed62e6504e9cb4adb670aed/html5/thumbnails/6.jpg)
Parallel.ForandParallel.Foreach
Understandingthetaskscheduler
Summary
5.C#LanguageSupportforAsynchrony
ImplementingthedownloadingofimagesfromBing
Creatingasimplesynchronoussolution
CreatingaparallelsolutionwithTaskParallelLibrary
EnhancingthecodewithC#5.0built-insupportforasynchrony
SimulatingC#asynchronousinfrastructurewithiterators
Istheasynckeywordreallyneeded?
Fire-and-forgettasks
OtherusefulTPLfeatures
Task.Delay
Task.Yield
Implementingacustomawaitabletype
Summary
6.UsingConcurrentDataStructures
Standardcollectionsandsynchronizationprimitives
ImplementingacachewithReaderWriterLockSlim
Concurrentcollectionsin.NET
ConcurrentDictionary
UsingLazy<T>
Implementationdetails
Lock-freeoperations
Fine-grainedlockoperations
Exclusivelockoperations
Usingtheimplementationdetailsinpractice
ConcurrentBag<T>
ConcurrentBaginpractice
ConcurrentQueue<T>
ConcurrentStack<T>
TheProducer/Consumerpattern
www.EBooksWorld.ir
![Page 7: Mastering C# Concurrency - EBooksWorld...a print book customer, you are entitled to a discount on the eBook copy. Get in touch with us at for more details](https://reader035.vdocument.in/reader035/viewer/2022062917/5ed62e6504e9cb4adb670aed/html5/thumbnails/7.jpg)
CustomProducer/Consumerpatternimplementation
TheProducer/Consumerpatternin.NET4.0+
Summary
7.LeveragingParallelPatterns
Concurrentidioms
ProcessTasksinCompletionOrder
Limitingtheparallelismdegree
Settingatasktimeout
Asynchronouspatterns
AsynchronousProgrammingModel
Event-basedAsynchronousPattern
Task-basedAsynchronousPattern
Concurrentpatterns
Parallelpipelines
Summary
8.Server-sideAsynchrony
Serverapplications
TheOWINWebAPIframework
Loadtestingandscalability
I/OandCPU-boundtasks
DeepdiveintoasynchronousI/O
RealandfakeasynchronousI/Ooperations
Synchronizationcontext
CPU-boundtasksandqueues
Summary
9.ConcurrencyintheUserInterface
TheimportanceofasynchronyforUI
UIthreadsandmessageloops
Commonproblemsandsolutions
Howtheawaitkeywordworks
Executionandsynchronizationcontexts
Performanceissues
www.EBooksWorld.ir
![Page 8: Mastering C# Concurrency - EBooksWorld...a print book customer, you are entitled to a discount on the eBook copy. Get in touch with us at for more details](https://reader035.vdocument.in/reader035/viewer/2022062917/5ed62e6504e9cb4adb670aed/html5/thumbnails/8.jpg)
Summary
10.TroubleshootingParallelPrograms
Howtroubleshootingparallelprogramsisdifferent
Heisenbugs
Writingtests
Loadtests
Unittests
Integrationtests
Debugging
Justmycodesetting
Callstackwindow
Threadswindow
Taskswindow
Parallelstackswindow
Performancemeasurementandprofiling
TheConcurrencyVisualizer
Summary
Index
www.EBooksWorld.ir
![Page 9: Mastering C# Concurrency - EBooksWorld...a print book customer, you are entitled to a discount on the eBook copy. Get in touch with us at for more details](https://reader035.vdocument.in/reader035/viewer/2022062917/5ed62e6504e9cb4adb670aed/html5/thumbnails/9.jpg)
www.EBooksWorld.ir
![Page 10: Mastering C# Concurrency - EBooksWorld...a print book customer, you are entitled to a discount on the eBook copy. Get in touch with us at for more details](https://reader035.vdocument.in/reader035/viewer/2022062917/5ed62e6504e9cb4adb670aed/html5/thumbnails/10.jpg)
MasteringC#Concurrency
www.EBooksWorld.ir
![Page 11: Mastering C# Concurrency - EBooksWorld...a print book customer, you are entitled to a discount on the eBook copy. Get in touch with us at for more details](https://reader035.vdocument.in/reader035/viewer/2022062917/5ed62e6504e9cb4adb670aed/html5/thumbnails/11.jpg)
www.EBooksWorld.ir
![Page 12: Mastering C# Concurrency - EBooksWorld...a print book customer, you are entitled to a discount on the eBook copy. Get in touch with us at for more details](https://reader035.vdocument.in/reader035/viewer/2022062917/5ed62e6504e9cb4adb670aed/html5/thumbnails/12.jpg)
MasteringC#ConcurrencyCopyright©2015PacktPublishing
Allrightsreserved.Nopartofthisbookmaybereproduced,storedinaretrievalsystem,ortransmittedinanyformorbyanymeans,withoutthepriorwrittenpermissionofthepublisher,exceptinthecaseofbriefquotationsembeddedincriticalarticlesorreviews.
Everyefforthasbeenmadeinthepreparationofthisbooktoensuretheaccuracyoftheinformationpresented.However,theinformationcontainedinthisbookissoldwithoutwarranty,eitherexpressorimplied.Neithertheauthors,norPacktPublishing,anditsdealersanddistributorswillbeheldliableforanydamagescausedorallegedtobecauseddirectlyorindirectlybythisbook.
PacktPublishinghasendeavoredtoprovidetrademarkinformationaboutallofthecompaniesandproductsmentionedinthisbookbytheappropriateuseofcapitals.However,PacktPublishingcannotguaranteetheaccuracyofthisinformation.
Firstpublished:October2015
Productionreference:1231015
PublishedbyPacktPublishingLtd.
LiveryPlace
35LiveryStreet
BirminghamB32PB,UK.
ISBN978-1-78528-665-0
www.packtpub.com
www.EBooksWorld.ir
![Page 13: Mastering C# Concurrency - EBooksWorld...a print book customer, you are entitled to a discount on the eBook copy. Get in touch with us at for more details](https://reader035.vdocument.in/reader035/viewer/2022062917/5ed62e6504e9cb4adb670aed/html5/thumbnails/13.jpg)
www.EBooksWorld.ir
![Page 14: Mastering C# Concurrency - EBooksWorld...a print book customer, you are entitled to a discount on the eBook copy. Get in touch with us at for more details](https://reader035.vdocument.in/reader035/viewer/2022062917/5ed62e6504e9cb4adb670aed/html5/thumbnails/14.jpg)
CreditsAuthors
EugeneAgafonov
AndrewKoryavchenko
Reviewers
TimGabrhel
MichaelBerantzinoHansen
GürayÖzen
SimonSoanes
AcquisitionEditor
ReshmaRaman
ContentDevelopmentEditor
ZeeyanPinheiro
TechnicalEditor
MenzaMathew
CopyEditors
KausambhiMajumdar
AlphaSingh
ProjectCoordinator
SuzanneCoutinho
Proofreader
SafisEditing
Indexer
RekhaNair
ProductionCoordinator
MelwynDsa
CoverWork
MelwynDsa
www.EBooksWorld.ir
![Page 15: Mastering C# Concurrency - EBooksWorld...a print book customer, you are entitled to a discount on the eBook copy. Get in touch with us at for more details](https://reader035.vdocument.in/reader035/viewer/2022062917/5ed62e6504e9cb4adb670aed/html5/thumbnails/15.jpg)
www.EBooksWorld.ir
![Page 16: Mastering C# Concurrency - EBooksWorld...a print book customer, you are entitled to a discount on the eBook copy. Get in touch with us at for more details](https://reader035.vdocument.in/reader035/viewer/2022062917/5ed62e6504e9cb4adb670aed/html5/thumbnails/16.jpg)
AbouttheAuthorsEugeneAgafonovleadstheLingvoLivedevelopmentdepartmentatABBYY,andhelivesandworksinMoscow.Hehasover15yearsofprofessionalexperienceinsoftwaredevelopmentandhasbeenworkingwithC#eversinceitwasinbetaversion.HehasbeenaMicrosoftMVPinASP.NETsince2006,andheoftenspeaksatlocalsoftwaredevelopmentconferences,suchasDevConRussia,aboutcutting-edgetechnologiesinmodernwebandserver-sideapplicationdevelopment.Hismainprofessionalinterestsarecloud-basedsoftwarearchitecture,scalability,andreliability.Eugeneisahugefanoffootballandplaystheguitarwithalocalrockband.Youcanreachhimathispersonalblogateugeneagafonov.comorhisTwitterhandleat@eugene_agafonov.
HealsowroteMultithreadinginC#5.0CookbookbyPacktPublishing.
IwouldliketothankSergeyTeplyakov,whoisasupercoolMicrosoftguyandhasanultimatetwitteraccountat@STeplyakov,forhelpingmealotinwritingchapters6and7,andhisinvaluableadvicethatallowedmetomakethisbookbetter.
AndrewKoryavchenkoisasoftwaredeveloperandanarchitectwholivesinMoscow,Russia.Heisoneofthefoundersofrsdn.ru—thelargestRussiansoftwaredevelopers’communityportal.
HisspecialtyisERPsystemsanddevelopertools.HeparticipatedinReSharperVisualStudioextensiondevelopment,whichisawell-knownproductivitytoolfor.NETdevelopers.Currently,heisworkingonparsingandcompilationtoolsfor.NETdevelopmentandalsosupportsanddevelopsthersdn.ruportal.
AndrewregularlyspeaksatonlineandofflineeventsandconferencesdedicatedtoMicrosofttechnologies,andhepublishesarticlesonsoftwaredevelopmenttopics.HealsousedtoteachEnterpriseSoftwareDevelopmentcourseinKubanStateUniversity.
AndrewhasbeenaMicrosoftMVPinC#since2005.
www.EBooksWorld.ir
![Page 17: Mastering C# Concurrency - EBooksWorld...a print book customer, you are entitled to a discount on the eBook copy. Get in touch with us at for more details](https://reader035.vdocument.in/reader035/viewer/2022062917/5ed62e6504e9cb4adb670aed/html5/thumbnails/17.jpg)
www.EBooksWorld.ir
![Page 18: Mastering C# Concurrency - EBooksWorld...a print book customer, you are entitled to a discount on the eBook copy. Get in touch with us at for more details](https://reader035.vdocument.in/reader035/viewer/2022062917/5ed62e6504e9cb4adb670aed/html5/thumbnails/18.jpg)
AbouttheReviewersTimGabrhelisaseniorapplicationdeveloperatConcurrencyInc.,withacorefocusonMicrosoftAzureandmodern.NETtechnologies.Heisacreatorandmakerandlovesbeinghands-onwithnewtechnologiesandmakingthemworkinreallife.TimhasbeenaconsultantforFortune100companies.Hehascontributedtothearchitectureandkeycomponentsofenterprisesolutionsthathavereachedhundredsofthousandsofusersaroundtheworld.YoucanfollowTimandhistechnicaljourneyathisblog,http://timgabrhel.com.
MichaelBerantzinoHansenisaMCPD.NETEnterpriseApplicationDeveloperspecializinginhighperformanceandefficientframeworks.Hehasbeenprogrammingsincethemid80sfromtheageof9.HestartedwithBasicandthenmovedontoC++.In1999,heearnedabachelorsdegreeincomputerscience,economics,andorganizationaldevelopment,whileworkingparttimeforground-breakingstartups.In2005,hemovedontoC#ashispreferredplatform.Michaelexcelsindevelopingcomplexframeworks,algorithms,andapplications.Hedoesfullstackdevelopmentusingmoderntechnologies.HerecentlyadoptedTypeScriptashispreferredplatformforclient-sidewebdevelopment.
MichaelcurrentlyworksasachiefsystemdeveloperintheSPAMfighter,developingcomplexe-mailanalysisplatformsresponsibleforallenterprisesolutionsinSPAMfighter.
GürayÖzenhasbeenworkingasaresearchfellowintheprogrammingmodelsteamatBarcelonaSupercomputingCenter(BSC)sinceAugust2013.HisworkisalsopartofhisPhDresearchthatexplorescompiler-basedparallelismandoptimizationsforheterogeneoussystems.Besidesthis,hiscurrentresearchinterestsconsistoftheprinciplesofprogramminglanguagesandparallelprogramming.Hereceivedamaster’sdegreeinhighperformancecomputingfromtheDepartmentofComputerArchitectureatUniversitatPolitècnicadeCatalunya–BarcelonaTechin2014.In2010and2012,heworkedatoneofthebiggestbanksinTurkeyasaC#-backedapplicationsdeveloper.Hehasabachelor’sdegreeincomputerscienceengineeringfromDokuzEylulUniveristyinIzmir,Turkey.
SimonSoanesisasoftwaredeveloperwithabackgroundinnetworkingtechnologies,databases,distributedsystems,anddebugging.EversincethedaysoftheC64,hehasenjoyedwritingsoftware,playingcomputergames,andmakingdevicescommunicatewitheachotherincreativeways.
He’scurrentlyworkinginthesouthofEnglandasacontractor.Atsomepoint,hebecameaddictedtosolvingtechnicalproblemsandautomatingthings.
Heoccasionallywritesablogathttp://www.nullify.net/.
www.EBooksWorld.ir
![Page 19: Mastering C# Concurrency - EBooksWorld...a print book customer, you are entitled to a discount on the eBook copy. Get in touch with us at for more details](https://reader035.vdocument.in/reader035/viewer/2022062917/5ed62e6504e9cb4adb670aed/html5/thumbnails/19.jpg)
www.EBooksWorld.ir
![Page 20: Mastering C# Concurrency - EBooksWorld...a print book customer, you are entitled to a discount on the eBook copy. Get in touch with us at for more details](https://reader035.vdocument.in/reader035/viewer/2022062917/5ed62e6504e9cb4adb670aed/html5/thumbnails/20.jpg)
www.PacktPub.com
www.EBooksWorld.ir
![Page 21: Mastering C# Concurrency - EBooksWorld...a print book customer, you are entitled to a discount on the eBook copy. Get in touch with us at for more details](https://reader035.vdocument.in/reader035/viewer/2022062917/5ed62e6504e9cb4adb670aed/html5/thumbnails/21.jpg)
Supportfiles,eBooks,discountoffers,andmoreForsupportfilesanddownloadsrelatedtoyourbook,pleasevisitwww.PacktPub.com.
DidyouknowthatPacktofferseBookversionsofeverybookpublished,withPDFandePubfilesavailable?YoucanupgradetotheeBookversionatwww.PacktPub.comandasaprintbookcustomer,youareentitledtoadiscountontheeBookcopy.Getintouchwithusat<[email protected]>formoredetails.
Atwww.PacktPub.com,youcanalsoreadacollectionoffreetechnicalarticles,signupforarangeoffreenewslettersandreceiveexclusivediscountsandoffersonPacktbooksandeBooks.
https://www2.packtpub.com/books/subscription/packtlib
DoyouneedinstantsolutionstoyourITquestions?PacktLibisPackt’sonlinedigitalbooklibrary.Here,youcansearch,access,andreadPackt’sentirelibraryofbooks.
www.EBooksWorld.ir
![Page 22: Mastering C# Concurrency - EBooksWorld...a print book customer, you are entitled to a discount on the eBook copy. Get in touch with us at for more details](https://reader035.vdocument.in/reader035/viewer/2022062917/5ed62e6504e9cb4adb670aed/html5/thumbnails/22.jpg)
Whysubscribe?FullysearchableacrosseverybookpublishedbyPacktCopyandpaste,print,andbookmarkcontentOndemandandaccessibleviaawebbrowser
www.EBooksWorld.ir
![Page 23: Mastering C# Concurrency - EBooksWorld...a print book customer, you are entitled to a discount on the eBook copy. Get in touch with us at for more details](https://reader035.vdocument.in/reader035/viewer/2022062917/5ed62e6504e9cb4adb670aed/html5/thumbnails/23.jpg)
FreeaccessforPacktaccountholdersIfyouhaveanaccountwithPacktatwww.PacktPub.com,youcanusethistoaccessPacktLibtodayandview9entirelyfreebooks.Simplyuseyourlogincredentialsforimmediateaccess.
www.EBooksWorld.ir
![Page 24: Mastering C# Concurrency - EBooksWorld...a print book customer, you are entitled to a discount on the eBook copy. Get in touch with us at for more details](https://reader035.vdocument.in/reader035/viewer/2022062917/5ed62e6504e9cb4adb670aed/html5/thumbnails/24.jpg)
InstantupdatesonnewPacktbooksGetnotified!Findoutwhennewbooksarepublishedbyfollowing@PacktEnterpriseonTwitterorthePacktEnterpriseFacebookpage.
ToMomandDad—youarethebestparentsonEarthandIloveyousomuch.
www.EBooksWorld.ir
![Page 25: Mastering C# Concurrency - EBooksWorld...a print book customer, you are entitled to a discount on the eBook copy. Get in touch with us at for more details](https://reader035.vdocument.in/reader035/viewer/2022062917/5ed62e6504e9cb4adb670aed/html5/thumbnails/25.jpg)
www.EBooksWorld.ir
![Page 26: Mastering C# Concurrency - EBooksWorld...a print book customer, you are entitled to a discount on the eBook copy. Get in touch with us at for more details](https://reader035.vdocument.in/reader035/viewer/2022062917/5ed62e6504e9cb4adb670aed/html5/thumbnails/26.jpg)
PrefaceRecentC#and.NETdevelopmentsinvolveimplicitlyusingasynchronyandconcurrency,evenwhenyouarenotawareofthem.ThiscanleadtofurtherproblemssincemanydetailsareusuallyhiddeninsidetheC#languageinfrastructureandthe.NETbaseclasslibraryAPIs.Toavoidproblemsandtobeabletocreaterobustapplications,adeveloperhastoknowexactlywhatisgoingonunderthehoodofasynchronyin.NET.
Besidesthis,itisimportanttounderstandyourgoalswhenwritingaconcurrentapplication.Ifitisrunningontheclient,itisusuallyagoodthingtouseallthecomputationalresourcesavailablesothattheapplicationbecomesasfastaspossible.ThisinvolveseffectivemultipleCPUcoresusage,andthusrequiresparallelprogrammingskills.However,iftheapplicationisrunningontheserver,itismoreimportantthattheserversupportsasmanyclientsaspossible,thantheperformanceofaconcreteclientrequestprocessing.Thisrequiresaprogrammertodistinguishasynchronyfrommultithreadingandhaveanunderstandingofscalability.
Allthesetopicswillbecoveredinthisbook,providingyouwithenoughinformationtoachieveasolidunderstandingofasynchronousandparallelprogramminginC#.Wewillstartwithbasicmultithreadingconcepts,reviewcommonconcurrentprogrammingproblemsandsolutions,andthenwewillgothroughC#and.NETsupportforwritingconcurrentapplications.Furtherinthebook,wewillcoverconcurrentdatastructuresandpatterns,andwewillreviewclient-sideandserver-sideconcurrencyissues.Attheendofthebook,wewilloutlinethebasicprinciplesforcreatingrobustconcurrentprograms.
www.EBooksWorld.ir
![Page 27: Mastering C# Concurrency - EBooksWorld...a print book customer, you are entitled to a discount on the eBook copy. Get in touch with us at for more details](https://reader035.vdocument.in/reader035/viewer/2022062917/5ed62e6504e9cb4adb670aed/html5/thumbnails/27.jpg)
WhatthisbookcoversChapter1,TraditionalConcurrency,coverscommonproblemswithmultithreadingandsolutionstotheseproblems.Youwillrefreshyourknowledgeaboutbasiclockingtechniquesandhowtomakelockingmoreefficient.
Chapter2,Lock-FreeConcurrency,goesfurtherintoperformanceoptimization.Itcoversvariouswaystowriteconcurrentprogramswithoutlocking,makingthecodefastandreliable.
Chapter3,UnderstandingParallelismGranularity,explainsanotherimportantaspectoforganizingyourparallelcode—splittingacomputationalworkloadbetweenthreads.Itintroducescoarse-grainedandfine-grainedapproaches,showingtheirprosandcons.
Chapter4,TaskParallelLibraryinDepth,goesintothedetailsofTaskParallelLibrary—aframeworktoorganizeyourconcurrentprogramasasetofrelatedtasks.YouwillfindtheinternalsofTPLreviewedandexplained.
Chapter5,C#LanguageSupportforAsynchrony,isadeepdiveintotheC#languageinfrastructure.Thechaptershowsexactlyhowtheasyncandawaitkeywordsworkandhowyoucanwriteyourownawait-compatiblecode.
Chapter6,UsingConcurrentDataStructures,coverstheuseofdatastructuresinaconcurrentprogramindetail,includingstandard.NETconcurrentcollectionsandcustomthreadsafecollectionsimplementations.
Chapter7,LeveragingParallelPatterns,reviewsprogrammingpatternsrelatedtoparallelapplications.Thechapterdescribesdifferentkindsofpatterns—historical.NETidioms,usefulcodesnippets,andahigh-levelparallelpipelinepattern.
Chapter8,Server-SideAsynchrony,isasolutiondescriptiontotheproblemofusingasynchronyontheserver.Itexplainswhyitisveryimportanttodistinguishasynchronyfromparallelism,andhowitcanaffectthescalabilityandreliabilityofyourserver.
Chapter9,ConcurrencyintheUserInterface,describesthedetailsofhowtheuserinterfaceisimplemented,whatamessageloopis,andwhyitisveryimportanttokeeptheUIthreadnonblocked.
Chapter10,TroubleshootingParallelPrograms,explainshowtofindoutwhatiswrongwithyourparallelprogram.Youwilllearnhowtowriteunittestsforanasynchronouscode,howtodebugit,andfindperformancebottlenecks.
www.EBooksWorld.ir
![Page 28: Mastering C# Concurrency - EBooksWorld...a print book customer, you are entitled to a discount on the eBook copy. Get in touch with us at for more details](https://reader035.vdocument.in/reader035/viewer/2022062917/5ed62e6504e9cb4adb670aed/html5/thumbnails/28.jpg)
www.EBooksWorld.ir
![Page 29: Mastering C# Concurrency - EBooksWorld...a print book customer, you are entitled to a discount on the eBook copy. Get in touch with us at for more details](https://reader035.vdocument.in/reader035/viewer/2022062917/5ed62e6504e9cb4adb670aed/html5/thumbnails/29.jpg)
WhatyouneedforthisbookYouwillneedVisualStudio2013or2015torunthecodesamples.Formostofthechapters,itwillbeenoughtousethefreeVisualStudioCommunity2013/2015editions,buttheperformancetestsampleswillrequiretheTest/UltimateorEnterpriseeditions.However,ifyoucannotusethis,itispossibletodownloadthefreeApachebenchtooltorunperformancetestsasdescribedinthebook.
www.EBooksWorld.ir
![Page 30: Mastering C# Concurrency - EBooksWorld...a print book customer, you are entitled to a discount on the eBook copy. Get in touch with us at for more details](https://reader035.vdocument.in/reader035/viewer/2022062917/5ed62e6504e9cb4adb670aed/html5/thumbnails/30.jpg)
www.EBooksWorld.ir
![Page 31: Mastering C# Concurrency - EBooksWorld...a print book customer, you are entitled to a discount on the eBook copy. Get in touch with us at for more details](https://reader035.vdocument.in/reader035/viewer/2022062917/5ed62e6504e9cb4adb670aed/html5/thumbnails/31.jpg)
WhothisbookisforMasteringC#ConcurrencyiswrittenforexistingC#developerswhohaveaknowledgeofbasicmultithreadingconceptsandwanttoimprovetheirasynchronousandparallelprogrammingskills.Thebookcoversdifferenttopics,frombasicconceptstocomplicatedprogrammingpatternsandalgorithmsusingtheC#and.NETecosystems.Thiswillbeusefultoserverandclientdevelopers,becauseitcoversalltheimportantaspectsofusingconcurrencyandasynchronyonbothsides.
www.EBooksWorld.ir
![Page 32: Mastering C# Concurrency - EBooksWorld...a print book customer, you are entitled to a discount on the eBook copy. Get in touch with us at for more details](https://reader035.vdocument.in/reader035/viewer/2022062917/5ed62e6504e9cb4adb670aed/html5/thumbnails/32.jpg)
www.EBooksWorld.ir
![Page 33: Mastering C# Concurrency - EBooksWorld...a print book customer, you are entitled to a discount on the eBook copy. Get in touch with us at for more details](https://reader035.vdocument.in/reader035/viewer/2022062917/5ed62e6504e9cb4adb670aed/html5/thumbnails/33.jpg)
ConventionsInthisbook,youwillfindanumberofstylesoftextthatdistinguishbetweendifferentkindsofinformation.Herearesomeexamplesofthesestyles,andanexplanationoftheirmeaning.
Codewordsintext,databasetablenames,foldernames,filenames,fileextensions,pathnames,dummyURLs,userinput,andTwitterhandlesareshownasfollows:“ThishappensbecausetheAddmethodoftheList<T>classisnotthreadsafe,andthereasonforthisliesintheimplementationdetails.”
Ablockofcodeissetasfollows:
publicvoidAdd(Titem)
{
if(_size==_items.Length)EnsureCapacity(_size+1);
_items[_size++]=item;
_version++;
}
Whenwewishtodrawyourattentiontoaparticularpartofacodeblock,therelevantlinesoritemsaresetinbold:
publicvoidAdd(Titem)
{
if(_size==_items.Length)EnsureCapacity(_size+1);
_items[_size++]=item;
_version++;
}
Anycommand-lineinputoroutputiswrittenasfollows:
T2:Add-[T2]:Item1
T1:Add-[T1]:Item1
T2:Add-[T2]:Item2
T2:Add-[T2]:Item3
Newtermsandimportantwordsareshowninbold.Wordsthatyouseeonthescreen,inmenusordialogboxesforexample,appearinthetextlikethis:“ClickonFinishandrepeatallthisforanothercontroller.”
NoteWarningsorimportantnotesappearinaboxlikethis.
TipTipsandtricksappearlikethis.
www.EBooksWorld.ir
![Page 34: Mastering C# Concurrency - EBooksWorld...a print book customer, you are entitled to a discount on the eBook copy. Get in touch with us at for more details](https://reader035.vdocument.in/reader035/viewer/2022062917/5ed62e6504e9cb4adb670aed/html5/thumbnails/34.jpg)
www.EBooksWorld.ir
![Page 35: Mastering C# Concurrency - EBooksWorld...a print book customer, you are entitled to a discount on the eBook copy. Get in touch with us at for more details](https://reader035.vdocument.in/reader035/viewer/2022062917/5ed62e6504e9cb4adb670aed/html5/thumbnails/35.jpg)
ReaderfeedbackFeedbackfromourreadersisalwayswelcome.Letusknowwhatyouthinkaboutthisbook—whatyoulikedormayhavedisliked.Readerfeedbackisimportantforustodeveloptitlesthatyoureallygetthemostoutof.
Tosendusgeneralfeedback,simplysendane-mailto<[email protected]>,andmentionthebooktitleviathesubjectofyourmessage.
Ifthereisatopicthatyouhaveexpertiseinandyouareinterestedineitherwritingorcontributingtoabook,seeourauthorguideonwww.packtpub.com/authors.
www.EBooksWorld.ir
![Page 36: Mastering C# Concurrency - EBooksWorld...a print book customer, you are entitled to a discount on the eBook copy. Get in touch with us at for more details](https://reader035.vdocument.in/reader035/viewer/2022062917/5ed62e6504e9cb4adb670aed/html5/thumbnails/36.jpg)
www.EBooksWorld.ir
![Page 37: Mastering C# Concurrency - EBooksWorld...a print book customer, you are entitled to a discount on the eBook copy. Get in touch with us at for more details](https://reader035.vdocument.in/reader035/viewer/2022062917/5ed62e6504e9cb4adb670aed/html5/thumbnails/37.jpg)
CustomersupportNowthatyouaretheproudownerofaPacktbook,wehaveanumberofthingstohelpyoutogetthemostfromyourpurchase.
www.EBooksWorld.ir
![Page 38: Mastering C# Concurrency - EBooksWorld...a print book customer, you are entitled to a discount on the eBook copy. Get in touch with us at for more details](https://reader035.vdocument.in/reader035/viewer/2022062917/5ed62e6504e9cb4adb670aed/html5/thumbnails/38.jpg)
DownloadingtheexamplecodeYoucandownloadtheexamplecodefilesforallPacktbooksyouhavepurchasedfromyouraccountathttp://www.packtpub.com.Ifyoupurchasedthisbookelsewhere,youcanvisithttp://www.packtpub.com/supportandregistertohavethefilese-maileddirectlytoyou.
www.EBooksWorld.ir
![Page 39: Mastering C# Concurrency - EBooksWorld...a print book customer, you are entitled to a discount on the eBook copy. Get in touch with us at for more details](https://reader035.vdocument.in/reader035/viewer/2022062917/5ed62e6504e9cb4adb670aed/html5/thumbnails/39.jpg)
ErrataAlthoughwehavetakeneverycaretoensuretheaccuracyofourcontent,mistakesdohappen.Ifyoufindamistakeinoneofourbooks—maybeamistakeinthetextorthecode—wewouldbegratefulifyouwouldreportthistous.Bydoingso,youcansaveotherreadersfromfrustrationandhelpusimprovesubsequentversionsofthisbook.Ifyoufindanyerrata,pleasereportthembyvisitinghttp://www.packtpub.com/submit-errata,selectingyourbook,clickingontheerratasubmissionformlink,andenteringthedetailsofyourerrata.Onceyourerrataareverified,yoursubmissionwillbeacceptedandtheerratawillbeuploadedonourwebsite,oraddedtoanylistofexistingerrata,undertheErratasectionofthattitle.Anyexistingerratacanbeviewedbyselectingyourtitlefromhttp://www.packtpub.com/support.
www.EBooksWorld.ir
![Page 40: Mastering C# Concurrency - EBooksWorld...a print book customer, you are entitled to a discount on the eBook copy. Get in touch with us at for more details](https://reader035.vdocument.in/reader035/viewer/2022062917/5ed62e6504e9cb4adb670aed/html5/thumbnails/40.jpg)
PiracyPiracyofcopyrightmaterialontheInternetisanongoingproblemacrossallmedia.AtPackt,wetaketheprotectionofourcopyrightandlicensesveryseriously.Ifyoucomeacrossanyillegalcopiesofourworks,inanyform,ontheInternet,pleaseprovideuswiththelocationaddressorwebsitenameimmediatelysothatwecanpursuearemedy.
Pleasecontactusat<[email protected]>withalinktothesuspectedpiratedmaterial.
Weappreciateyourhelpinprotectingourauthors,andourabilitytobringyouvaluablecontent.
www.EBooksWorld.ir
![Page 41: Mastering C# Concurrency - EBooksWorld...a print book customer, you are entitled to a discount on the eBook copy. Get in touch with us at for more details](https://reader035.vdocument.in/reader035/viewer/2022062917/5ed62e6504e9cb4adb670aed/html5/thumbnails/41.jpg)
QuestionsYoucancontactusat<[email protected]>ifyouarehavingaproblemwithanyaspectofthebook,andwewilldoourbesttoaddressit.
www.EBooksWorld.ir
![Page 42: Mastering C# Concurrency - EBooksWorld...a print book customer, you are entitled to a discount on the eBook copy. Get in touch with us at for more details](https://reader035.vdocument.in/reader035/viewer/2022062917/5ed62e6504e9cb4adb670aed/html5/thumbnails/42.jpg)
www.EBooksWorld.ir
![Page 43: Mastering C# Concurrency - EBooksWorld...a print book customer, you are entitled to a discount on the eBook copy. Get in touch with us at for more details](https://reader035.vdocument.in/reader035/viewer/2022062917/5ed62e6504e9cb4adb670aed/html5/thumbnails/43.jpg)
Chapter1.TraditionalConcurrencySpeakingofconcurrency,wehavetostarttalkingaboutthreads.Ironically,thereasonbehindimplementingthreadswastoisolateprogramsfromeachother.BackintheearlydaysofWindows,versions3.*usedcooperativemultitasking.Thismeantthattheoperatingsystemexecutedalltheprogramsonasingleexecutionloop,andifoneofthoseprogramshung,everyotherprogramandtheoperatingsystemitselfwouldstoprespondingaswellandthenitwouldberequiredtorebootthemachinetoresolvethisproblem.
Tocreateamorerobustenvironment,theOShadtolearnhowtogiveeveryprogramitsownpieceofCPU,soifoneprogramenteredaninfiniteloop,theotherswouldstillbeabletousetheCPUfortheirownneeds.Athreadisanimplementationofthisconcept.Thethreadsallowimplementingpreemptivemultitasking,whereinsteadoftheapplicationdecidingwhentoyieldcontroltoanotherapplication,theOScontrolshowmuchCPUtimetogivetoeachapplication.
WhenCPUsstartedtohavemultiplecores,itbecamemorebeneficialtomakefulluseofthecomputationalcapabilityavailable.Theuseofthethreadsdirectlybyapplicationssuddenlybecamemoreworthwhile.However,whenexploringmultithreadingissues,suchashowtosharethedatabetweenthethreadssafely,theset-uptimeofthethreadsimmediatelybecomeevident.
Inthischapter,wewillconsiderthebasicconcurrentprogrammingpitfallsandthetraditionalapproachtodealwiththem.
www.EBooksWorld.ir
![Page 44: Mastering C# Concurrency - EBooksWorld...a print book customer, you are entitled to a discount on the eBook copy. Get in touch with us at for more details](https://reader035.vdocument.in/reader035/viewer/2022062917/5ed62e6504e9cb4adb670aed/html5/thumbnails/44.jpg)
What’stheproblem?Simplyusingmultiplethreadsinaprogramisnotaverycomplicatedtask.Ifyourprogramcanbeeasilyseparatedintoseveralindependenttasks,thenyoujustrunthemindifferentthreads,andthesethreadscanbescaledalongwiththenumberofCPUcores.However,usuallyrealworldprogramsrequiresomeinteractionbetweenthesethreads,suchasexchanginginformationtocoordinatetheirwork.Thiscannotbeimplementedwithoutsharingsomedata,whichrequiresallocatingsomeRAMspaceinsuchawaythatitisaccessiblefromallthethreads.Dealingwiththissharedstateistherootofalmosteveryproblemrelatedtoparallelprogramming.
Thefirstcommonproblemwithsharedstateisundefinedaccessorder.Ifwehavereadandwriteaccess,thisleadstoincorrectcalculationresults.Thissituationiscommonlyreferredtoasaracecondition.
Followingisasampleofaracecondition.Wehaveacounter,whichisbeingchangedfromdifferentthreadssimultaneously.Eachthreadincrementsthecounter,thendoessomework,andthendecrementsthecounter.
constintiterations=10000;
varcounter=0;
ThreadStartproc=()=>{
for(inti=0;i<iterations;i++){
counter++;
Thread.SpinWait(100);
counter--;
}
};
varthreads=Enumerable
.Range(0,8)
.Select(n=>newThread(proc))
.ToArray();
foreach(varthreadinthreads)
thread.Start();
foreach(varthreadinthreads)
thread.Join();
Console.WriteLine(counter);
Theexpectedcountervalueis0.However,whenyouruntheprogram,yougetdifferentnumbers(whichisusuallynot0,butitcouldbe)eachtime.Thereasonisthatincrementinganddecrementingthecounterisnotanatomicoperation,butconsistsofthreeseparatesteps–readingthecountervalue,incrementingordecrementingthisvalue,andwritingtheresultbackintothecounter.
Letusassumethatwehaveinitialcountervalue0,andtwothreads.Thefirstthreadreads0,incrementsitto1,andwrites1intothecounter.Thesecondthreadreads1fromthecounter,incrementsitto2,andthenwrites2intothecounter.Thisseemstobecorrectandisexactlywhatweexpected.Thisscenarioisrepresentedinthefollowingdiagram:
www.EBooksWorld.ir
![Page 45: Mastering C# Concurrency - EBooksWorld...a print book customer, you are entitled to a discount on the eBook copy. Get in touch with us at for more details](https://reader035.vdocument.in/reader035/viewer/2022062917/5ed62e6504e9cb4adb670aed/html5/thumbnails/45.jpg)
Nowthefirstthreadreads2fromthecounter,andatthesametimeitdecrementsitto1;thesecondthreadreads2fromthecounter,becausethefirstthreadhasn’twritten1intothecounteryet.Sonow,thefirstthreadwrites1intothecounter,andthesecondthreaddecrements2to1andwritesthevalue1intothecounter.Asaresult,wehavethevalue1,whilewe’reexpecting0.Thisscenarioisrepresentedinthefollowingdiagram:
TipDownloadingtheexamplecode
Youcandownloadtheexamplecodefilesfromyouraccountathttp://www.packtpub.comforallthePacktPublishingbooksyouhavepurchased.Ifyoupurchasedthisbookelsewhere,youcanvisithttp://www.packtpub.com/supportandregistertohavethefilese-maileddirectlytoyou.
Toavoidthis,wehavetorestrictaccesstothecountersothatonlyonethreadreadsitatatime,calculatestheresult,andwritesitback.Sucharestrictioniscalledalock.However,byusingittoresolvearaceconditionproblem,wecreateotherpossibilitiesforourconcurrentcodetofail.Withsucharestriction,weturnourparallelprocessintoasequentialprocess,whichinturnmeansthatourcoderunslessefficiently.Themoretimethecoderunsinsidethelock,thelessefficientandscalablethewholeprogramis.Thisisbecausethelockheldbyonethreadblockstheotherthreadsfromperformingtheirwork,therebymakingthewholeprogramtakelongertorun.So,wehavetominimizethelocktimetokeeptheotherthreadsrunning,insteadofwaitingforthelocktobereleasedto
www.EBooksWorld.ir
![Page 46: Mastering C# Concurrency - EBooksWorld...a print book customer, you are entitled to a discount on the eBook copy. Get in touch with us at for more details](https://reader035.vdocument.in/reader035/viewer/2022062917/5ed62e6504e9cb4adb670aed/html5/thumbnails/46.jpg)
startdoingtheircalculations.
Anotherproblemrelatedtolocksisbestillustratedbythefollowingexample.Itshowstwothreadsusingtworesources,AandB.ThefirstthreadneedstolockobjectAfirst,thenB,whilethesecondthreadstartswithlockingBandthenA.
constintcount=10000;
vara=newobject();
varb=newobject();
varthread1=
newThread(
()=>
{
for(inti=0;i<count;i++)
lock(a)
lock(b)
Thread.SpinWait(100);
});
varthread2=
newThread(
()=>
{
for(inti=0;i<count;i++)
lock(b)
lock(a)
Thread.SpinWait(100);
});
thread1.Start();
thread2.Start();
thread1.Join();
thread2.Join();
Console.WriteLine("Done");
Itlookslikethiscodeisalright,butifyourunitseveraltimes,itwilleventuallyhang.Thereasonforthisliesinanissuewiththelockingorder.IfthefirstthreadlocksA,andthesecondlocksBbeforethefirstthreaddoes,thenthesecondthreadstartswaitingforthelockonAtobereleased.However,toreleasethelockonA,thefirstthreadneedstoputalockonB,whichisalreadylockedbythesecondthread.Therefore,boththethreadswillwaitforeverandtheprogramwillhang.
Suchasituationiscalledadeadlock.Itisusuallyquitehardtodiagnosedeadlocks,becauseitishardtoreproduceone.
NoteThebestwaytoavoiddeadlocksistotakepreventivemeasureswhenwritingcode.Thebestpracticeistoavoidcomplicatedlockstructuresandnestedlocks,andminimizethetimeinlocks.Ifyoususpecttherecouldbeadeadlock,thenthereisanotherwaytopreventitfromhappening,whichisbysettingatimeoutforacquiringalock.
www.EBooksWorld.ir
![Page 47: Mastering C# Concurrency - EBooksWorld...a print book customer, you are entitled to a discount on the eBook copy. Get in touch with us at for more details](https://reader035.vdocument.in/reader035/viewer/2022062917/5ed62e6504e9cb4adb670aed/html5/thumbnails/47.jpg)
www.EBooksWorld.ir
![Page 48: Mastering C# Concurrency - EBooksWorld...a print book customer, you are entitled to a discount on the eBook copy. Get in touch with us at for more details](https://reader035.vdocument.in/reader035/viewer/2022062917/5ed62e6504e9cb4adb670aed/html5/thumbnails/48.jpg)
UsinglocksTherearedifferenttypesoflocksinC#and.NET.Wewillcovertheselaterinthechapter,andalsothroughoutthebook.LetusstartwiththemostcommonwaytousealockinC#,whichisalockstatement.
www.EBooksWorld.ir
![Page 49: Mastering C# Concurrency - EBooksWorld...a print book customer, you are entitled to a discount on the eBook copy. Get in touch with us at for more details](https://reader035.vdocument.in/reader035/viewer/2022062917/5ed62e6504e9cb4adb670aed/html5/thumbnails/49.jpg)
LockstatementLockstatementinC#usesasingleargument,whichcouldbeaninstanceofanyclass.Thisinstancewillrepresentthelockitself.
Readingotherpeople’scodes,youcouldseethatalockusestheinstanceofcollectionorclass,whichcontainsshareddata.Itisnotagoodpractice,becausesomeoneelsecouldusethisobjectforlocking,andpotentiallycreateadeadlocksituation.So,itisrecommendedtouseaspecialprivatesynchronizationobject,thesolepurposeofwhichistoserveasaconcretelock:
//Bad
lock(myCollection){
myCollection.Add(data);
}
//Good
lock(myCollectionLock){
myCollection.Add(data);
}`
NoteItisdangeroustouselock(this)andlock(typeof(MyType)).Thebasicideawhyitisbadremainsthesame:theobjectsyouarelockingcouldbepubliclyaccessible,andthussomeoneelsecouldacquirealockonitcausingadeadlock.However,usingthethiskeywordmakesthesituationmoreimplicit;ifsomeoneelsemadetheobjectpublic,itwouldbeveryhardtotrackthatitisbeingusedinsidealock.
Lockingthetypeobjectisevenworse.Inthecurrentversionsof.NET,theruntimetypeobjectscouldbesharedacrossapplicationdomains(runninginthesameprocess).Itispossiblebecausethoseobjectsareimmutable.However,thismeansthatadeadlockcouldbecaused,notonlybyanotherthread,butalsobyANOTHERAPPLICATION,andIbetthatyouwouldhardlyunderstandwhat’sgoingoninsuchacase.
FollowingishowwecanrewritethefirstexamplewithraceconditionandfixitusingC#lockstatement.Nowthecodewillbeasfollows:
constintiterations=10000;
varcounter=0;
varlockFlag=newobject();
ThreadStartproc=()=>{
for(inti=0;i<iterations;i++)
{
lock(lockFlag)
counter++;
Thread.SpinWait(100);
lock(lockFlag)
counter--;
}
};
varthreads=Enumerable
.Range(0,8)
.Select(n=>newThread(proc))
www.EBooksWorld.ir
![Page 50: Mastering C# Concurrency - EBooksWorld...a print book customer, you are entitled to a discount on the eBook copy. Get in touch with us at for more details](https://reader035.vdocument.in/reader035/viewer/2022062917/5ed62e6504e9cb4adb670aed/html5/thumbnails/50.jpg)
.ToArray();
foreach(varthreadinthreads)
thread.Start();
foreach(varthreadinthreads)
thread.Join();
Console.WriteLine(counter);
Nowthiscodeworksproperly,andtheresultisalways0.
Tounderstandwhatishappeningwhenalockstatementisusedintheprogram,letuslookattheIntermediateLanguagecode,whichisaresultofcompilingC#program.ConsiderthefollowingC#code:
staticvoidMain()
{
varctr=0;
varlockFlag=newobject();
lock(lockFlag)
ctr++;
}
Theprecedingblockofcodewillbecompiledintothefollowing:
.methodprivatehidebysigstaticvoidMain()cilmanaged{
.entrypoint
//Codesize48(0x30)
.maxstack2
.localsinit([0]int32ctr,
[1]objectlockFlag,
[2]bool'<>s__LockTaken0',
[3]objectCS$2$0000,
[4]boolCS$4$0001)
IL_0000:nop
IL_0001:ldc.i4.0
IL_0002:stloc.0
IL_0003:newobjinstancevoid[mscorlib]System.Object::.ctor()
IL_0008:stloc.1
IL_0009:ldc.i4.0
IL_000a:stloc.2
.try
{
IL_000b:ldloc.1
IL_000c:dup
IL_000d:stloc.3
IL_000e:ldloca.s'<>s__LockTaken0'
IL_0010:callvoid
[mscorlib]System.Threading.Monitor::Enter(object,bool&)
IL_0015:nop
IL_0016:ldloc.0
IL_0017:ldc.i4.1
IL_0018:add
IL_0019:stloc.0
IL_001a:leave.sIL_002e
}//end.try
finally
{
IL_001c:ldloc.2
www.EBooksWorld.ir
![Page 51: Mastering C# Concurrency - EBooksWorld...a print book customer, you are entitled to a discount on the eBook copy. Get in touch with us at for more details](https://reader035.vdocument.in/reader035/viewer/2022062917/5ed62e6504e9cb4adb670aed/html5/thumbnails/51.jpg)
IL_001d:ldc.i4.0
IL_001e:ceq
IL_0020:stloc.sCS$4$0001
IL_0022:ldloc.sCS$4$0001
IL_0024:brtrue.sIL_002d
IL_0026:ldloc.3
IL_0027:callvoid
[mscorlib]System.Threading.Monitor::Exit(object)
IL_002c:nop
IL_002d:endfinally
}//endhandler
IL_002e:nop
IL_002f:ret
}//endofmethodProgram::Main
ThiscanbeexplainedwithdecompilationtoC#.Itwilllooklikethis:
staticvoidMain()
{
varctr=0;
varlockFlag=newobject();
boollockTaken=false;
try
{
System.Threading.Monitor.Enter(lockFlag,reflockTaken);
ctr++;
}
finally
{
if(lockTaken)
System.Threading.Monitor.Exit(lockFlag);
}
}
ItturnsoutthatthelockstatementturnsintocallingtheMonitor.EnterandMonitor.Exitmethods,wrappedintoatry-finallyblock.TheEntermethodacquiresanexclusivelockandreturnsaboolvalue,indicatingthatalockwassuccessfullyacquired.Ifsomethingwentwrong,forexampleanexceptionhasbeenthrown,theboolvaluewouldbesettofalse,andtheExitmethodwouldreleasetheacquiredlock.
Atry-finallyblockensuresthattheacquiredlockwillbereleasedevenifanexceptionoccursinsidethelockstatement.IftheEntermethodindicatesthatwecannotacquirealock,thentheExitmethodwillnotbeexecuted.
www.EBooksWorld.ir
![Page 52: Mastering C# Concurrency - EBooksWorld...a print book customer, you are entitled to a discount on the eBook copy. Get in touch with us at for more details](https://reader035.vdocument.in/reader035/viewer/2022062917/5ed62e6504e9cb4adb670aed/html5/thumbnails/52.jpg)
MonitorclassTheMonitorclasscontainsotherusefulmethodsthathelpustowriteconcurrentcode.OneofsuchmethodsistheTryEntermethod,whichallowstheprovisionofatimeoutvaluetoit.Ifalockcouldnotbeobtainedbeforethetimeoutisexpired,theTryEntermethodwouldreturnfalse.Thisisquiteanefficientmethodtopreventdeadlocks,butyouhavetowritesignificantlymorecode.
ConsiderthepreviousdeadlocksamplerefactoredinawaythatoneofthethreadsusesMonitor.TryEnterinsteadoflock:
staticvoidMain()
{
constintcount=10000;
vara=newobject();
varb=newobject();
varthread1=newThread(
()=>{
for(inti=0;i<count;i++)
lock(a)
lock(b)
Thread.SpinWait(100);
});
varthread2=newThread(()=>LockTimeout(a,b,count));
thread1.Start();
thread2.Start();
thread1.Join();
thread2.Join();
Console.WriteLine("Done");
}
staticvoidLockTimeout(objecta,objectb,intcount)
{
boolaccquiredB=false;
boolaccquiredA=false;
constintwaitSeconds=5;
constintretryCount=3;
for(inti=0;i<count;i++)
{
intretries=0;
while(retries<retryCount)
{
try
{
accquiredB=Monitor.TryEnter(b,
TimeSpan.FromSeconds(waitSeconds));
if(accquiredB){
try{
accquiredA=Monitor.TryEnter(a,
TimeSpan.FromSeconds(waitSeconds));
if(accquiredA){
Thread.SpinWait(100);
break;
}
www.EBooksWorld.ir
![Page 53: Mastering C# Concurrency - EBooksWorld...a print book customer, you are entitled to a discount on the eBook copy. Get in touch with us at for more details](https://reader035.vdocument.in/reader035/viewer/2022062917/5ed62e6504e9cb4adb670aed/html5/thumbnails/53.jpg)
else{
retries++;
}
}
finally{
if(accquiredA){
Monitor.Exit(a);
}
}
}
else{
retries++;
}
}
finally{
if(accquiredB)
Monitor.Exit(b);
}
}
if(retries>=retryCount)
Console.WriteLine("couldnotobtainlocks");
}
}
IntheLockTimeoutmethod,weimplementedaretrystrategy.Foreachloopiteration,wetrytoacquirelockBfirst,andifwecannotdosoin5seconds,wetryagain.IfwehavesuccessfullyacquiredlockB,thenweinturntrytoacquirelockA,andifwewaitforitformorethan5seconds,wetryagaintoacquireboththelocks.ThisguaranteesthatifsomeonewaitsendlesslytoacquirealockonB,thenthisoperationwilleventuallysucceed.
IfwedonotsucceedacquiringlockB,thenwetryagainforadefinednumberofattempts.Theneitherwesucceed,orweadmitthatwecannotobtaintheneededlocksandgotothenextiteration.
Inaddition,theMonitorclasscanbeusedtoorchestratemultiplethreadsintoaworkflowwiththeWait,Pulse,andPulseAllmethods.WhenamainthreadcallstheWaitmethod,thecurrentlockisreleased,andthethreadisblockeduntilsomeotherthreadcallsthePulseorPulseAllmethods.Thisallowsthecoordinationthedifferentthreadsexecutionintosomesortofsequence.
Asimpleexampleofsuchworkflowiswhenwehavetwothreads:themainthreadandanadditionalthreadthatperformssomecalculation.Wewouldliketopausethemainthreaduntilthesecondthreadfinishesitswork,andthengetbacktothemainthread,andinturnblockthisadditionalthreaduntilwehaveotherdatatocalculate.Thiscanbeillustratedbythefollowingcode:
vararg=0;
varresult="";
varcounter=0;
varlockHandle=newobject();
varcalcThread=newThread(()=>{
while(true)
lock(lockHandle)
www.EBooksWorld.ir
![Page 54: Mastering C# Concurrency - EBooksWorld...a print book customer, you are entitled to a discount on the eBook copy. Get in touch with us at for more details](https://reader035.vdocument.in/reader035/viewer/2022062917/5ed62e6504e9cb4adb670aed/html5/thumbnails/54.jpg)
{
counter++;
result=arg.ToString();
Monitor.Pulse(lockHandle);
Monitor.Wait(lockHandle);
}
})
{
IsBackground=true
};
lock(lockHandle)
{
calcThread.Start();
Thread.Sleep(100);
Console.WriteLine("counter={0},result={1}",counter,result);
arg=123;
Monitor.Pulse(lockHandle);
Monitor.Wait(lockHandle);
Console.WriteLine("counter={0},result={1}",counter,result);
arg=321;
Monitor.Pulse(lockHandle);
Monitor.Wait(lockHandle);
Console.WriteLine("counter={0},result={1}",counter,result);
}
Asaresultofrunningthisprogram,wewillgetthefollowingoutput:
counter=0,result=
counter=1,result=123
counter=2,result=321
Atfirst,westartacalculationthread.Thenweprinttheinitialvaluesforcounterandresult,andthenwecallPulse.Thisputsthecalculationthreadintoaqueuecalledreadyqueue.Thismeansthatthisthreadisreadytoacquirethislockassoonasitgetsreleased.ThenwecalltheWaitmethod,whichreleasesthelockandputsthemainthreadintoawaitingqueue.Thefirstthreadinthereadyqueue,whichisourcalculationthread,acquiresthelockandstartstowork.Aftercompletingitscalculations,thesecondthreadcallsPulse,whichmovesathreadattheheadofthewaitingqueue(whichisourmainthread)intothereadyqueue.Ifthereareseveralthreadsinthewaitingqueue,onlythefirstonewouldgointothereadyqueue.Toputallthethreadsintothereadyqueueatonce,wecouldusethePulseAllmethod.So,whenthesecondthreadcallsWait,ourmainthreadreacquiresthelock,changesthecalculationdata,andrepeatsthewholeprocessonemoretime.
NoteNotethatwecanusetheWait,Pulse,andPulseAllmethodsonlywhenthecurrentthreadownsalock.TheWaitmethodcouldblockindefinitelyincasenootherthreadscallPulseorPulseAll,soitcanbeareasonforadeadlock.Topreventdeadlocks,wecanspecifyatimeoutvaluetotheWaitmethodtobeabletoreactincasewecannotreacquirethelockforacertaintimeperiod.
www.EBooksWorld.ir
![Page 55: Mastering C# Concurrency - EBooksWorld...a print book customer, you are entitled to a discount on the eBook copy. Get in touch with us at for more details](https://reader035.vdocument.in/reader035/viewer/2022062917/5ed62e6504e9cb4adb670aed/html5/thumbnails/55.jpg)
www.EBooksWorld.ir
![Page 56: Mastering C# Concurrency - EBooksWorld...a print book customer, you are entitled to a discount on the eBook copy. Get in touch with us at for more details](https://reader035.vdocument.in/reader035/viewer/2022062917/5ed62e6504e9cb4adb670aed/html5/thumbnails/56.jpg)
Reader-writerlockItisverycommontoseesamplesofcodewherethesharedstateisoneofthestandard.NETcollections:List<T>orDictionary<K,V>.Thesecollectionsarenotthreadsafe;thusweneedsynchronizationtoorganizeconcurrentaccess.
Therearespecialconcurrentcollectionsthatcanbeusedinsteadofthestandardlistanddictionarytoachievethreadsafety.WewillreviewtheminChapter6,UsingConcurrentDataStructures.Fornow,letusassumethatwehavereasonstoorganizeconcurrentaccessbyourselves.
Theeasiestwaytoachievesynchronizationistousethelockoperatorwhenreadingandwritingfromthesecollections.However,theMSDNdocumentationstatesthatifacollectionisnotmodifiedwhilebeingread,synchronizationisnotrequired:
ItissafetoperformmultiplereadoperationsonaList<T>,butissuescanoccurifthecollectionismodifiedwhileit’sbeingread.
AnotherimportantMSDNpagestatesthefollowingregardingacollection:
ADictionary<TKey,TValue>cansupportmultiplereadersconcurrently,aslongasthecollectionisnotmodified.
Thismeansthatwecanperformthereadoperationsfrommultiplethreadsifthecollectionisnotbeingmodified.Thisallowsustoavoidexcessivelocking,andminimizesperformanceoverheadandpossibledeadlocksinsuchsituations.
Toleveragethis,thereisastandard.NETFrameworkclass,System.Threading.ReaderWriterLock.Itprovidesthreetypesoflocks:toreadsomethingfromaresource,towritesomething,andaspecialonetoupgradethereaderlocktoawriterlock.Thefollowingmethodpairsrepresenttheselocks:AcquireReaderLock/ReleaseReaderLock,AcquireWriterLock/ReleaseWriterLock,andUpgradeToWriterLock/DowngradeFromWriterLock,correspondingly.Itisalsopossibletoprovideatimeoutvalue,afterwhichtherequesttoacquirethelockwillexpire.Providingthe-1valuemeansthatalockhasnotimeout.
NoteItisimportanttoalwaysreleasealockafteracquiringit.Alwaysputthecodeforreleasingalockintothefinallyblockofthetry/catchstatement,otherwiseanyexceptionthrownbeforereleasingthislockwouldleavetheReaderWriterLockobjectinalockedstate,preventinganyfurtheraccesstothislock.
Areaderlockputsathreadintheblockedstateonlywhenthereisatleastonewriterlockacquired.Otherwise,norealthreadblockinghappens.Awriterlockwaitsuntileveryotherlockisreleased,andtheninturnitpreventstheacquiringofanyotherlocks,untilit’sreleased.
Upgradingalockisuseful;wheninsideanopenreaderlock,weneedtowritesomethingintoacollection.Forexample,wefirstcheckifthereisanentrywithsomekeyinthe
www.EBooksWorld.ir
![Page 57: Mastering C# Concurrency - EBooksWorld...a print book customer, you are entitled to a discount on the eBook copy. Get in touch with us at for more details](https://reader035.vdocument.in/reader035/viewer/2022062917/5ed62e6504e9cb4adb670aed/html5/thumbnails/57.jpg)
dictionary,andinsertthisentryifitdoesnotexist.Acquiringawriterlockwouldbeinefficient,sincetherecouldbenowriteoperation,soitisoptimaltousethisupgradescenario.
Notethatusinganykindoflockisstillnotasefficientasasimplecheck,anditmakessensetousepatternssuchasdouble-checkedlocking.Considerthefollowcodesnippet:
if(writeRequiredCondition)
{
_rwLock.AcquireWriterLock();
try
{
if(writeRequiredCondition)
//dowrite
}
finally
{
_rwLock.ReleaseWriterLock();
}
}
TheReaderWriterLockclasshasanestedlockscounter,anditavoidscreatinganewlockwhentryingtoacquireitwheninsideanotherlock.Insuchacase,thelockcounterisincrementedandthendecrementedwhenthenestedlockisreleased.Thereallockisacquiredonlywhenthiscounterisequaltoto0.
Nevertheless,thisimplementationhassomeseriousdrawbacks.First,itusesthreadblocking,whichisquiteperformancecostly,andbesidesthat,addsitsownadditionaloverhead.Inaddition,ifthewriteoperationisveryshort,thenusingReaderWriterLockcouldbeevenworsethansimplylockingthecollectionforeveryoperation.Inadditiontothat,themethodnamesandsemanticsarenotintuitive,whichmakesreadingandunderstandingthecodemuchharder.
Thisisthereasonwhythenewimplementation,System.Threading.ReaderWriterLockSlim,wasintroducedin.NETFramework3.5.ItshouldalwaysbeusedinsteadofReaderWriterLockforthefollowingreasons:
Itismoreefficient,especiallywithshortlocks.Methodnamesbecamemoreintuitive:EnterReadLock/ExitReadLock,EnterWriteLock/ExitWriteLock,andEnterUpgradeableReadLock/ExitUpgradeableReadLock.Ifwetrytoacquireawriterlockinsideareaderlock,itwillbeanupgradebydefault.Insteadofusingatimeoutvalue,separatemethodshavebeenadded:TryEnterReadLock,TryEnterWriteLock,andTryEnterUpgradeableReadLock,whichmakethecodecleaner.Usingnestedlocksisnowforbiddenbydefault.Itispossibletoallownestedlocksbyspecifyingaconstructorparameter,butusingnestedlocksisusuallyamistakeandthisbehaviorhelpstoexplicitlydeclarehowitisintendedtodealwiththem.Internalenhancementshelptoimproveperformanceandavoiddeadlocks.
ThefollowingisanexampleofdifferentlockingstrategiesforDictionary<K,V>inthemultiplereaders/singlewriterscenario.First,wedefinehowmanyreadersandwriters
www.EBooksWorld.ir
![Page 58: Mastering C# Concurrency - EBooksWorld...a print book customer, you are entitled to a discount on the eBook copy. Get in touch with us at for more details](https://reader035.vdocument.in/reader035/viewer/2022062917/5ed62e6504e9cb4adb670aed/html5/thumbnails/58.jpg)
we’regoingtohave,howlongareadandwriteoperationwilltake,andhowmanytimestorepeatthoseoperations.
staticclassProgram
{
privateconstint_readersCount=5;
privateconstint_writersCount=1;
privateconstint_readPayload=100;
privateconstint_writePayload=100;
privateconstint_count=100000;
Thenwedefinethecommontestlogic.Thetargetdictionaryisbeingcreatedalongwiththereaderandwritermethods.ThemethodcalledMeasureusesLINQtomeasuretheperformanceofconcurrentaccess.
privatestaticreadonlyDictionary<int,string>_map=newDictionary<int,
string>();
privatestaticvoidReaderProc()
{
stringval;
_map.TryGetValue(Environment.TickCount%_count,outval);
//Dosomework
Thread.SpinWait(_readPayload);
}
privatestaticvoidWriterProc()
{
varn=Environment.TickCount%_count;
//Dosomework
Thread.SpinWait(_writePayload);
_map[n]=n.ToString();
}
privatestaticlongMeasure(Actionreader,Actionwriter)
{
varthreads=Enumerable
.Range(0,_readersCount)
.Select(n=>newThread(
()=>{
for(inti=0;i<_count;i++)
reader();
}))
.Concat(Enumerable
.Range(0,_writersCount)
.Select(n=>newThread(
()=>{
for(inti=0;i<_count;i++)
writer();
})))
.ToArray();
_map.Clear();
varsw=Stopwatch.StartNew();
foreach(varthreadinthreads)
thread.Start();
www.EBooksWorld.ir
![Page 59: Mastering C# Concurrency - EBooksWorld...a print book customer, you are entitled to a discount on the eBook copy. Get in touch with us at for more details](https://reader035.vdocument.in/reader035/viewer/2022062917/5ed62e6504e9cb4adb670aed/html5/thumbnails/59.jpg)
foreach(varthreadinthreads)
thread.Join();
sw.Stop();
returnsw.ElapsedMilliseconds;
}
Thenweusesimplelocktosynchronizeconcurrentaccesstothedictionary:
privatestaticreadonlyobject_simpleLockLock=newobject();
privatestaticvoidSimpleLockReader()
{
lock(_simpleLockLock)
ReaderProc();
}
privatestaticvoidSimpleLockWriter()
{
lock(_simpleLockLock)
WriterProc();
}
ThesecondtestisusinganolderReaderWriterLockclassasfollows:
privatestaticreadonlyReaderWriterLock_rwLock=new
ReaderWriterLock();
privatestaticvoidRWLockReader()
{
_rwLock.AcquireReaderLock(-1);
try
{
ReaderProc();
}
finally
{
_rwLock.ReleaseReaderLock();
}
}
privatestaticvoidRWLockWriter()
{
_rwLock.AcquireWriterLock(-1);
try
{
WriterProc();
}
finally
{
_rwLock.ReleaseWriterLock();
}
}
Finally,we’lldemonstratetheusageofReaderWriterLockSlim:
privatestaticreadonlyReaderWriterLockSlim_rwLockSlim=new
www.EBooksWorld.ir
![Page 60: Mastering C# Concurrency - EBooksWorld...a print book customer, you are entitled to a discount on the eBook copy. Get in touch with us at for more details](https://reader035.vdocument.in/reader035/viewer/2022062917/5ed62e6504e9cb4adb670aed/html5/thumbnails/60.jpg)
ReaderWriterLockSlim();
privatestaticvoidRWLockSlimReader()
{
_rwLockSlim.EnterReadLock();
try
{
ReaderProc();
}
finally
{
_rwLockSlim.ExitReadLock();
}
}
privatestaticvoidRWLockSlimWriter()
{
_rwLockSlim.EnterWriteLock();
try
{
WriterProc();
}
finally
{
_rwLockSlim.ExitWriteLock();
}
}
Nowwerunallofthesetests,usingoneiterationasawarmuptoexcludeanyfirstrunissuesthatcouldaffecttheoverallperformance:
staticvoidMain()
{
//Warmup
Measure(SimpleLockReader,SimpleLockWriter);
//Measure
varsimpleLockTime=Measure(SimpleLockReader,SimpleLockWriter);
Console.WriteLine("Simplelock:{0}ms",simpleLockTime);
//Warmup
Measure(RWLockReader,RWLockWriter);
//Measure
varrwLockTime=Measure(RWLockReader,RWLockWriter);
Console.WriteLine("ReaderWriterLock:{0}ms",rwLockTime);
//Warmup
Measure(RWLockSlimReader,RWLockSlimWriter);
//Measure
varrwLockSlimTime=Measure(RWLockSlimReader,RWLockSlimWriter);
Console.WriteLine("ReaderWriterLockSlim:{0}ms",rwLockSlimTime);
}
}
www.EBooksWorld.ir
![Page 61: Mastering C# Concurrency - EBooksWorld...a print book customer, you are entitled to a discount on the eBook copy. Get in touch with us at for more details](https://reader035.vdocument.in/reader035/viewer/2022062917/5ed62e6504e9cb4adb670aed/html5/thumbnails/61.jpg)
ExecutingthiscodeonCorei72600Kandx64OSintheReleaseconfigurationgivesthefollowingresults:
Simplelock:367ms
ReaderWriterLock:246ms
ReaderWriterLockSlim:183ms
ItshowsthatReaderWriterLockSlimisabout2timesfasterthantheusuallockstatement.
Youcanchangethenumberofreaderandwriterthreads,tweakthelocktime,andseehowtheperformancechangesineachcase.
NoteNotethatusingareaderwriterlockonthecollectionisnotenoughtoprovideapossibilitytoiterateoverthiscollection.Whilethecollectionitselfwillbeinthecorrectstate,whileiterating,ifanyofthecollectionitemswereremovedoradded,anexceptionwillbethrown.Thismeans,thatyouneedtoputalltheiterationprocessinsidealock,orproduceanewimmutablecopyofthecollectionanditerateoverthiscopy.
www.EBooksWorld.ir
![Page 62: Mastering C# Concurrency - EBooksWorld...a print book customer, you are entitled to a discount on the eBook copy. Get in touch with us at for more details](https://reader035.vdocument.in/reader035/viewer/2022062917/5ed62e6504e9cb4adb670aed/html5/thumbnails/62.jpg)
www.EBooksWorld.ir
![Page 63: Mastering C# Concurrency - EBooksWorld...a print book customer, you are entitled to a discount on the eBook copy. Get in touch with us at for more details](https://reader035.vdocument.in/reader035/viewer/2022062917/5ed62e6504e9cb4adb670aed/html5/thumbnails/63.jpg)
SpinlockUsingoperatingsystemlevelsynchronizationprimitivesrequiresquiteanoticeableamountofresources,becauseofthecontextswitchingandalltheentirecorrespondingoverhead.Besidesthis,thereissuchthingaslocklatency;thatis,thetimerequiredforalocktobenotifiedaboutthestatechangeofanotherlock.Thismeansthatwhenthecurrentlockisbeingreleased,ittakessomeadditionaltimeforanotherlocktobesignaled.Thisisthereasonwhywhenweneedshorttimelocks,itcouldbesignificantlyfastertouseasinglethreadwithoutanylocksthantoparallelizetheseoperationsusingOSlevellockingmechanics.
Toavoidunnecessarycontextswitchesinsuchasituation,wecanusealoop,whichcheckstheotherlocksineachiteration.Sincethelocksshouldbeveryshort,wewouldnotusetoomuchCPU,andwehaveasignificantperformanceboostbynotusingtheoperatingsystemresourcesandbyloweringlocklatencytothelowestamount.
Thispatternisnotsoeasytoimplement,and,tobeeffective,youwouldneedtousespecificCPUinstructions.Fortunately,thereisastandardimplementationofthispatterninthe.NETFrameworkstartingwithversion3.5.Theimplementationcontainsthefollowingmethodsandclasses:
www.EBooksWorld.ir
![Page 64: Mastering C# Concurrency - EBooksWorld...a print book customer, you are entitled to a discount on the eBook copy. Get in touch with us at for more details](https://reader035.vdocument.in/reader035/viewer/2022062917/5ed62e6504e9cb4adb670aed/html5/thumbnails/64.jpg)
Thread.SpinWaitThread.SpinWaitjustspinsaninfiniteloop.It’slikeThread.Sleep,onlywithoutcontextswitchingandusingCPUtime.Itisusedrarelyincommonscenarios,butcouldbeusefulinsomespecificcases,suchassimulatingrealCPUwork.
www.EBooksWorld.ir
![Page 65: Mastering C# Concurrency - EBooksWorld...a print book customer, you are entitled to a discount on the eBook copy. Get in touch with us at for more details](https://reader035.vdocument.in/reader035/viewer/2022062917/5ed62e6504e9cb4adb670aed/html5/thumbnails/65.jpg)
System.Threading.SpinWaitSystem.Threading.SpinWaitisastructureimplementingaloopwithaconditioncheck.Itisusedinternallyinspinlockimplementation.
www.EBooksWorld.ir
![Page 66: Mastering C# Concurrency - EBooksWorld...a print book customer, you are entitled to a discount on the eBook copy. Get in touch with us at for more details](https://reader035.vdocument.in/reader035/viewer/2022062917/5ed62e6504e9cb4adb670aed/html5/thumbnails/66.jpg)
System.Threading.SpinLockHerewewillbediscussingaboutthespinlockimplementationitself.
NotethatitisastructurewhichallowstosaveonclassinstanceallocationandreducesGCoverhead.
Thespinlockcanoptionallyuseamemorybarrier(oramemoryfencinginstruction)tonotifyotherthreadsthatthelockhasbeenreleased.Thedefaultbehavioristouseamemorybarrier,whichpreventsmemoryaccessoperationreorderingbycompilerorhardware,andimprovesthefairnessofthelockattheexpenseofperformance.Theothercaseisfaster,butcouldleadtoincorrectbehaviorinsomesituations.
Usually,it’snotencouragedtouseaspinlockdirectlyunlessyouare100%surewhatyou’redoing.Makesurethatyouhaveconfirmedtheperformancebottleneckwithtestsandyouknowthatyourlocksarereallyshort.
Thecodeinsideaspinlockshouldnotdothefollowing:
Useregularlocks,oracodethatuseslocksAcquiremorethanonespinlockatatimePerformdynamicdispatchedcalls(virtualmethods,interfacemethods,ordelegatecalls)Callanythird-partycode,whichisnotcontrolledbyyouPerformmemoryallocation,includingnewoperatorusage
Thefollowingisasampletestforaspinlock:
staticclassProgram
{
privateconstint_count=10000000;
staticvoidMain()
{
//Warmup
varmap=newDictionary<double,double>();
varr=Math.Sin(0.01);
//lock
map.Clear();
varprm=0d;
varlockFlag=newobject();
varsw=Stopwatch.StartNew();
for(inti=0;i<_count;i++)
lock(lockFlag)
{
map.Add(prm,Math.Sin(prm));
prm+=0.01;
}
sw.Stop();
Console.WriteLine("Lock:{0}ms",sw.ElapsedMilliseconds);
//spinlockwithmemorybarrier
map.Clear();
varspinLock=newSpinLock();
www.EBooksWorld.ir
![Page 67: Mastering C# Concurrency - EBooksWorld...a print book customer, you are entitled to a discount on the eBook copy. Get in touch with us at for more details](https://reader035.vdocument.in/reader035/viewer/2022062917/5ed62e6504e9cb4adb670aed/html5/thumbnails/67.jpg)
prm=0;
sw=Stopwatch.StartNew();
for(inti=0;i<_count;i++)
{
vargotLock=false;
try
{
spinLock.Enter(refgotLock);
map.Add(prm,Math.Sin(prm));
prm+=0.01;
}
finally
{
if(gotLock)
spinLock.Exit(true);
}
}
sw.Stop();
Console.WriteLine("Spinlockwithmemorybarrier:{0}ms",
sw.ElapsedMilliseconds);
//spinlockwithoutmemorybarrier
map.Clear();
prm=0;
sw=Stopwatch.StartNew();
for(inti=0;i<_count;i++)
{
vargotLock=false;
try
{
spinLock.Enter(refgotLock);
map.Add(prm,Math.Sin(prm));
prm+=0.01;
}
finally
{
if(gotLock)
spinLock.Exit(false);
}
}
sw.Stop();
Console.WriteLine("Spinlockwithoutmemorybarrier:{0}ms",
sw.ElapsedMilliseconds);
}
}
ExecutingthiscodeonCorei72600Kandx64OSinReleaseconfigurationgivesthefollowingresults:
Lock:1906ms
Spinlockwithmemorybarrier:1761ms
Spinlockwithoutmemorybarrier:1731ms
Notethattheperformanceboostisverysmallevenwithshortdurationlocks.Alsonotethatstartingfrom.NETFramework3.5,theMonitor,ReaderWriterLock,andReaderWriterLockSlimclassesareimplementedwithspinlock.
www.EBooksWorld.ir
![Page 68: Mastering C# Concurrency - EBooksWorld...a print book customer, you are entitled to a discount on the eBook copy. Get in touch with us at for more details](https://reader035.vdocument.in/reader035/viewer/2022062917/5ed62e6504e9cb4adb670aed/html5/thumbnails/68.jpg)
NoteThemaindisadvantageofspinlocksisintensiveCPUusage.Theendlessloopconsumesenergy,whiletheblockedthreaddoesnot.However,nowthestandardMonitorclasscanusespinlockforashorttimelockandthenturntousuallock,soinrealworldscenariosthedifferencewouldbeevenlessnoticeablethaninthistest.
www.EBooksWorld.ir
![Page 69: Mastering C# Concurrency - EBooksWorld...a print book customer, you are entitled to a discount on the eBook copy. Get in touch with us at for more details](https://reader035.vdocument.in/reader035/viewer/2022062917/5ed62e6504e9cb4adb670aed/html5/thumbnails/69.jpg)
www.EBooksWorld.ir
![Page 70: Mastering C# Concurrency - EBooksWorld...a print book customer, you are entitled to a discount on the eBook copy. Get in touch with us at for more details](https://reader035.vdocument.in/reader035/viewer/2022062917/5ed62e6504e9cb4adb670aed/html5/thumbnails/70.jpg)
OptimizationstrategyCreatingparallelalgorithmsisnotasimpletask:thereisnouniversalsolutiontoit.Ineverycase,youhavetouseaspecificapproachtowriteeffectivecode.However,thereareseveralsimplerulesthatworkformostoftheparallelprograms.
www.EBooksWorld.ir
![Page 71: Mastering C# Concurrency - EBooksWorld...a print book customer, you are entitled to a discount on the eBook copy. Get in touch with us at for more details](https://reader035.vdocument.in/reader035/viewer/2022062917/5ed62e6504e9cb4adb670aed/html5/thumbnails/71.jpg)
LocklocalizationThefirstthingtotakeintoaccountwhenwritingparallelcodeistolockaslittlecodeaspossible,andensurethatthecodeinsidethelockrunsasfastaspossible.Thismakesitlessdeadlock-proneandscalebetterwiththenumberofCPUcores.Tosumup,acquirethelockaslateaspossibleandreleaseitassoonaspossible.
Letusconsiderthefollowingsituation:forexample,wehavesomecalculationperformedbymethodCalcwithoutanysideeffects.Wewouldliketocallitwithseveraldifferentargumentsandstoretheresultsinalist.Thefirstintentionistowritethecodeasfollows:
for(vari=from;i<from+count;i++)
lock(_result)
_result.Add(Calc(i));
Thiscodeworks,butwecalltheCalcmethodandperformthecalculationinsideourlock.Thiscalculationdoesnothaveanysideeffects,andthusrequiresnolocking,soitwouldbemuchmoreefficienttorewritethecodeasshownnext:
for(vari=from;i<from+count;i++)
{
varcalc=Calc(i);
lock(_result)
_result.Add(calc);
}
Ifthecalculationtakesasignificantamountoftime,thenthisimprovementcouldmakethecoderunseveraltimesfaster.
www.EBooksWorld.ir
![Page 72: Mastering C# Concurrency - EBooksWorld...a print book customer, you are entitled to a discount on the eBook copy. Get in touch with us at for more details](https://reader035.vdocument.in/reader035/viewer/2022062917/5ed62e6504e9cb4adb670aed/html5/thumbnails/72.jpg)
ShareddataminimizationAnotherwayofimprovingparallelcodeperformanceisbyminimizingtheshareddata,whichisbeingwritteninparallel.Itisacommonsituationwhenwelockoverthewholecollectioneverytimewewriteintoit,insteadofthinkingandloweringtheamountoflocksandthedatabeinglocked.Organizingconcurrentaccessanddatastorageinawaythatitminimizesthenumberoflockscanleadtoasignificantperformanceincrease.
Inthepreviousexample,welockedtheentirecollectioneachtime,asdescribedinthepreviousparagraph.However,wereallydon’tcareaboutwhichworkerthreadprocessesexactlywhatpieceofinformation,sowecouldrewritethepreviouscodelikethefollowing:
vartempRes=newList<string>(count);
for(vari=from;i<from+count;i++)
{
varcalc=Calc(i);
tempRes.Add(calc);
}
lock(_result)
_result.AddRange(tempRes);
Thefollowingisthecompletecomparison:
staticclassProgram
{
privateconstint_count=1000000;
privateconstint_threadCount=8;
privatestaticreadonlyList<string>_result=newList<string>();
privatestaticstringCalc(intprm)
{
Thread.SpinWait(100);
returnprm.ToString();
}
privatestaticvoidSimpleLock(intfrom,intcount)
{
for(vari=from;i<from+count;i++)
lock(_result)
_result.Add(Calc(i));
}
privatestaticvoidMinimizedLock(intfrom,intcount)
{
for(vari=from;i<from+count;i++)
{
varcalc=Calc(i);
lock(_result)
_result.Add(calc);
}
}
privatestaticvoidMinimizedSharedData(intfrom,intcount)
www.EBooksWorld.ir
![Page 73: Mastering C# Concurrency - EBooksWorld...a print book customer, you are entitled to a discount on the eBook copy. Get in touch with us at for more details](https://reader035.vdocument.in/reader035/viewer/2022062917/5ed62e6504e9cb4adb670aed/html5/thumbnails/73.jpg)
{
vartempRes=newList<string>(count);
for(vari=from;i<from+count;i++)
{
varcalc=Calc(i);
tempRes.Add(calc);
}
lock(_result)
_result.AddRange(tempRes);
}
privatestaticlongMeasure(Func<int,ThreadStart>actionCreator)
{
_result.Clear();
varthreads=
Enumerable
.Range(0,_threadCount)
.Select(n=>newThread(actionCreator(n)))
.ToArray();
varsw=Stopwatch.StartNew();
foreach(varthreadinthreads)
thread.Start();
foreach(varthreadinthreads)
thread.Join();
sw.Stop();
returnsw.ElapsedMilliseconds;
}
staticvoidMain()
{
//Warmup
SimpleLock(1,1);
MinimizedLock(1,1);
MinimizedSharedData(1,1);
constintpart=_count/_threadCount;
vartime=Measure(n=>()=>SimpleLock(n*part,part));
Console.WriteLine("Simplelock:{0}ms",time);
time=Measure(n=>()=>MinimizedLock(n*part,part));
Console.WriteLine("Minimizedlock:{0}ms",time);
time=Measure(n=>()=>MinimizedSharedData(n*part,part));
Console.WriteLine("Minimizedshareddata:{0}ms",time);
}
}
ExecutingthiscodeonCorei72600Kandx64OSinReleaseconfigurationgivesthefollowingresults:
Simplelock:806ms
Minimizedlock:321ms
Minimizedshareddata:165ms
www.EBooksWorld.ir
![Page 74: Mastering C# Concurrency - EBooksWorld...a print book customer, you are entitled to a discount on the eBook copy. Get in touch with us at for more details](https://reader035.vdocument.in/reader035/viewer/2022062917/5ed62e6504e9cb4adb670aed/html5/thumbnails/74.jpg)
www.EBooksWorld.ir
![Page 75: Mastering C# Concurrency - EBooksWorld...a print book customer, you are entitled to a discount on the eBook copy. Get in touch with us at for more details](https://reader035.vdocument.in/reader035/viewer/2022062917/5ed62e6504e9cb4adb670aed/html5/thumbnails/75.jpg)
SummaryInthischapter,welearnedabouttheissueswithusingshareddatafrommultiplethreads.Welookedthroughthedifferenttechniquesallowingustoorganizeconcurrentaccesstosharedstatemoreefficientlyindifferentscenarios.Wealsoestablishedanunderstandingabouttheperformanceissuesofusinglocks,threadblocking,andcontextswitching.
Inthenextchapter,wewillcontinuetoexploreconcurrentaccesstoshareddata.However,thistimewewilltrytoavoidlocksandmakeourparallelprogrammorerobustandefficient.
www.EBooksWorld.ir
![Page 76: Mastering C# Concurrency - EBooksWorld...a print book customer, you are entitled to a discount on the eBook copy. Get in touch with us at for more details](https://reader035.vdocument.in/reader035/viewer/2022062917/5ed62e6504e9cb4adb670aed/html5/thumbnails/76.jpg)
www.EBooksWorld.ir
![Page 77: Mastering C# Concurrency - EBooksWorld...a print book customer, you are entitled to a discount on the eBook copy. Get in touch with us at for more details](https://reader035.vdocument.in/reader035/viewer/2022062917/5ed62e6504e9cb4adb670aed/html5/thumbnails/77.jpg)
Chapter2.Lock-FreeConcurrencyInChapter1,TraditionalConcurrency,wereviewedthreadsynchronizationwithlockingandhowtouselockseffectively.However,therewillbestillperformanceoverheadrelatedtolocking.Thebestwaytoavoidsuchissuesisbynotusinglocksatallwheneverpossible.Algorithmsthatdonotuselockingarereferredtoaslock-freealgorithms.
Lock-freealgorithmsinturnareofdifferenttypes.Oneofthemostimportanttypesiswait-freealgorithms.Thesealgorithmsnotonlyevadetheuseoflocks,butalsoareguaranteedtonotwaitforanyeventsfromotherthreads.Thisisabest-casescenariobutunfortunately,itisararesituationwhenwecanavoidwaitingfortheotherthreadsatall.Usually,arealconcurrentprogramtriestobeascloseaspossibletowait-free,andthisiswhateverydevelopershouldtrytoachieve.
ThereisonemorecategoryofalgorithmsthatdonotuseOS-levelthreadblockingbutusespinlocks.Thisallowsthecreationofquiteefficientcodeinsituationswhenthecodeinsidethelockhastorunveryfast.Suchalgorithmscanbecalledlock-freeinvarioussources,butstrictlyspeakingtheyarenotastheydonotguaranteethatthealgorithmwillbeprogressing,sinceitispossibleitgetsblockedinvarioussituations.WewilldiscusssuchsituationslaterinChapter10,TroubleshootingParallelPrograms.
NotePleasenoticethatamultithreadedprogramcanbetargetedindifferentscenarios,andthusthemetricscouldbedifferent.Forexample,ifourgoalistosavethebatterychargeofalaptoportosavetheCPUworkload,lockingtechniquesarepreferred(untilsomepointwhentherewillbetoomanyblockedthreads).However,ifweneedoverallperformance,thenlock-freealgorithmsareusuallybetter.
www.EBooksWorld.ir
![Page 78: Mastering C# Concurrency - EBooksWorld...a print book customer, you are entitled to a discount on the eBook copy. Get in touch with us at for more details](https://reader035.vdocument.in/reader035/viewer/2022062917/5ed62e6504e9cb4adb670aed/html5/thumbnails/78.jpg)
MemorymodelandcompileroptimizationsMemorymodelandcompileroptimizationsarenotdirectlyrelatedtoconcurrency,buttheyareveryimportantconceptsforanyonewhocreatesconcurrentcode,shownasfollows:
classProgram
{
bool_loop=true;
staticvoidMain(string[]args)
{
varp=newProgram();
Task.Run(()=>
{
Thread.Sleep(100);
p._loop=false;
});
while(p._loop);
//while(p._loop){Console.Write(".");};
Console.WriteLine("Exitedtheloop");
}
}
IfyoucompilethiswiththeReleasebuildconfigurationandJITcompileroptimizationsenabled,theloopwillusuallyhangonthex86andx64architectures.ThishappensbecauseJIToptimizesthep._loopreadanddoessomethinglikethis:
if(p._loop)
{
while(true);
}
Ifthereissomethinginsidethewhileloop,JITwillprobablynotoptimizethiscodeinthisway.Also,wemayusethevolatilekeywordwiththeBooleanflaglikethis:
volatilebool_loop;
Inthiscase,JITwillturnoffthisoptimizationaswell.Thisiswhereweuseamemorymodel,anditgetscomplicatedhere.HereisaquotefromtheC#languagespecification:
Fornon-volatilefields,optimizationtechniquesthatreorderinstructionscanleadtounexpectedandunpredictableresultsinmulti-threadedprogramsthataccessfieldswithoutsynchronizationsuchasthatprovidedbythelock-statement.Theseoptimizationscanbeperformedbythecompiler,bytherun-timesystem,orbyhardware.Forvolatilefields,suchreorderingoptimizationsarerestricted:
Areadofavolatilefieldiscalledavolatileread.Avolatilereadhas“acquiresemantics”;thatis,itisguaranteedtooccurpriortoanyreferencestomemorythatoccurafteritintheinstructionsequence.
www.EBooksWorld.ir
![Page 79: Mastering C# Concurrency - EBooksWorld...a print book customer, you are entitled to a discount on the eBook copy. Get in touch with us at for more details](https://reader035.vdocument.in/reader035/viewer/2022062917/5ed62e6504e9cb4adb670aed/html5/thumbnails/79.jpg)
Awriteofavolatilefieldiscalledavolatilewrite.Avolatilewritehas“releasesemantics”;thatis,itisguaranteedtohappenafteranymemoryreferencespriortothewriteinstructionintheinstructionsequence.
Aswecansee,thereisnothingspecificallystatedhereaboutcompileroptimizations,butinfactJITdoesnotoptimizevolatilefieldreadinthiscase.
Sowecanseeadescriptioninaspecification,buthowdoesthisreallywork?Let’slookatavolatilereadexample:
classVolatileRead
{
int_x;
volatileint_y;
int_z;
voidRead()
{
intx=_x;//1
inty=_y;//2(volatile)
intz=_z;//3
}
}
Thepossiblereorderingoptionswouldbe1,2,3(original);2,1,3;and2,3,1.Thiscanbeimaginedasaone-wayfencethatallowstheprecedingoperationtopassthrough,butdoesnotallowsubsequentoperations.Sothisiscalledtheacquirefence.
Volatilewriteslookprettysimilar.Considerthefollowingcodesnippet:
classVolatileWrite
{
int_x;
volatileint_y;
int_z;
voidRead()
{
_x=1;//1
_y=2;//2(volatile)
_z=3;//3
}
}
Possibleoptionshereare1,2,3(original);1,3,2;and3,1,2.Thisisthereleasefence,whichallowsthereorderingofonlysubsequentreadorwriteoperationsbutdoesnotallowtheprecedingwriteoperation.WehavetheThread.VolatileReadandThread.VolatileWritemethodsthatdothesamethingexplicitly.ThereistheThread.MemoryBarrier(memorybarrier)methodaswell,whichallowsustouseafullfencewhenwedonotletthroughanyoperations.
Iwouldliketomentionthatwearenowonlesscertainground.Differentmemorymodelsondifferentarchitecturescanbeconfusing,andcodewithoutvolatilecanperfectlyworkonx86andamd64.However,ifyouareusingshareddata,pleasebeawareofpossible
www.EBooksWorld.ir
![Page 80: Mastering C# Concurrency - EBooksWorld...a print book customer, you are entitled to a discount on the eBook copy. Get in touch with us at for more details](https://reader035.vdocument.in/reader035/viewer/2022062917/5ed62e6504e9cb4adb670aed/html5/thumbnails/80.jpg)
reorderingandnon-reorderingoptimizationsandchoosetheappropriatebehavior.
NotePleasebeawarethatmakingafieldvolatilemeansthatallthereadandwriteoperationswillhaveslightlylowerperformanceandtheywillhavethecodeincommon,sincesomepossibleoptimizationswillbeignored.
www.EBooksWorld.ir
![Page 81: Mastering C# Concurrency - EBooksWorld...a print book customer, you are entitled to a discount on the eBook copy. Get in touch with us at for more details](https://reader035.vdocument.in/reader035/viewer/2022062917/5ed62e6504e9cb4adb670aed/html5/thumbnails/81.jpg)
www.EBooksWorld.ir
![Page 82: Mastering C# Concurrency - EBooksWorld...a print book customer, you are entitled to a discount on the eBook copy. Get in touch with us at for more details](https://reader035.vdocument.in/reader035/viewer/2022062917/5ed62e6504e9cb4adb670aed/html5/thumbnails/82.jpg)
TheSystem.Threading.InterlockedclassWhenwereviewedraceconditionsinthepreviouschapter,welearnedthatevenasimpleincrementoperationconsistsofthreeseparateactions.AlthoughmodernCPUscanperformsuchoperationsatonce,itisnecessarytomakethemsafetobeusedinconcurrentprograms.
The.NETFrameworkcontainstheSystem.Threading.Interlockedclassthatprovidesaccesstoseveraloperationsthatareatomic,whichmeansthattheyareuninterruptibleandappeartooccurinstantaneouslytotherestofthesystem.Thesearetheoperationsthatthelock-freealgorithmsarebasedon.
Let’srevisearaceconditionexampleandcomparethelockingandInterlockedclassoperations.First,wewillusethetraditionallockingapproach:
varcounterLock=newobject();
varcounter=0;
ThreadStartproc=
()=>
{
for(inti=0;i<count;i++)
{
lock(counterLock)
counter++;
Thread.SpinWait(100);
lock(counterLock)
counter--;
}
};
varthreads=
Enumerable
.Range(0,8)
.Select(n=>newThread(proc))
.ToArray();
varsw=Stopwatch.StartNew();
foreach(varthreadinthreads)
thread.Start();
foreach(varthreadinthreads)
thread.Join();
sw.Stop();
Console.WriteLine("Locks:counter={0},time={1}ms",counter,
sw.ElapsedMilliseconds);
Now,let’sreplacelockingwiththeInterlockedclassmethodcalls:
counter=0;
ThreadStartproc2=
()=>
{
for(inti=0;i<count;i++)
{
Interlocked.Increment(refcounter);
Thread.SpinWait(100);
Interlocked.Decrement(refcounter);
www.EBooksWorld.ir
![Page 83: Mastering C# Concurrency - EBooksWorld...a print book customer, you are entitled to a discount on the eBook copy. Get in touch with us at for more details](https://reader035.vdocument.in/reader035/viewer/2022062917/5ed62e6504e9cb4adb670aed/html5/thumbnails/83.jpg)
}
};
threads=
Enumerable
.Range(0,8)
.Select(n=>newThread(proc2))
.ToArray();
sw=Stopwatch.StartNew();
foreach(varthreadinthreads)
thread.Start();
foreach(varthreadinthreads)
thread.Join();
sw.Stop();
Console.WriteLine("Lockfree:counter={0},time={1}ms",counter,
sw.ElapsedMilliseconds);
Asaresult,wegotthisonareferencecomputer:
Locks:counter=0,time=1892ms
Locks:counter=0,time=800ms
Justusingatomicoperationsperformedmorethantwiceaswellandkepttheprogramlogiccorrect.
Anothertrickypartis64-bitintegercalculations.Whentheprogramrunsinthe64-bitmode,thereadandwriteoperationsfor64-bitintegernumbersareatomic.However,whenrunninginthe32-bitmode,theseoperationsbecomenonatomicandconsistoftwoparts—reading/writinghigh32bitsandlow32bitsofthenumber.
TheInterlockedclasscontainstheReadmethodthatcanreada64-bitintegerinthe32-bitmodeasanatomicoperation.Thisisnotrequiredin64-bitmode,butifyoucompileyourprograminanyCPUmodethenyoushouldusethismethodtoguaranteeatomicityofreads.TherearetheIncrementandDecrementmethodoverloadsfor64-bitintegersaswell,andthereistheAddmethodthatallowsustohaveatomicadditionof32-bitand64-bitintegers.
Anotherveryimportantoperationisthevalueexchange.Lookingatthefollowingcodeitisobviousthatthisoperationisnotatomic,andthuswemustputthiscodeinsidesomekindoflocktokeepthisoperationcorrectinaconcurrentprogram:
vartmp=a;
a=b;
b=tmp;
TheInterlockedclassallowsustoperformthisoperationasatomicwiththeExchangemethod:
b=Interlocked.Exchange(refa,b)
Thereareseveraloverloadsforthismethodthatallowustoexchangethenumericvaluesofdifferenttypesincluding32-bitand64-bitintegers,thefloatanddoublevalues,objectreferences(thereisagenericversionofthismethodwiththetypeparameter),andtheIntPtrstructures.
ThemostcomplicatedatomicoperationprovidedbytheInterlockedclassisthe
www.EBooksWorld.ir
![Page 84: Mastering C# Concurrency - EBooksWorld...a print book customer, you are entitled to a discount on the eBook copy. Get in touch with us at for more details](https://reader035.vdocument.in/reader035/viewer/2022062917/5ed62e6504e9cb4adb670aed/html5/thumbnails/84.jpg)
CompareExchangemethod.Itacceptsthreearguments,thenitcomparesthefirstargumentwiththethird;iftheyareequal,itassignsthesecondargumentvaluetothefirstargument.Thisisperformedbyspecialinstructiononhardwaretoo.Wewillseeanexampleofthislaterinthischapterwhenwetrytoimplementalock-freequeue.
NoteAlltheInterlockedclassmethodcallsimplicitlygeneratefullfences.
www.EBooksWorld.ir
![Page 85: Mastering C# Concurrency - EBooksWorld...a print book customer, you are entitled to a discount on the eBook copy. Get in touch with us at for more details](https://reader035.vdocument.in/reader035/viewer/2022062917/5ed62e6504e9cb4adb670aed/html5/thumbnails/85.jpg)
www.EBooksWorld.ir
![Page 86: Mastering C# Concurrency - EBooksWorld...a print book customer, you are entitled to a discount on the eBook copy. Get in touch with us at for more details](https://reader035.vdocument.in/reader035/viewer/2022062917/5ed62e6504e9cb4adb670aed/html5/thumbnails/86.jpg)
InterlockedinternalsTounderstandhowinterlockedinternalsworkunderthehood,we’regoingtoseewhatmachinecodeisbeinggeneratedwhencompilingtheInterlocked.Incrementmethod.Ifwejustruntheprogramindebugmodeandlookatthedisassemblywindow,wewillseetheusualmethodcall.
Toseewhatisreallygoingon,wehavetoenablealloptimizations:
1. First,weneedtobuildthecodeintheReleasemodeinVisualStudio.2. Then,wehavetogotoTools|Options|Debugging|Generalanduncheckthe
SuppressJIToptimizationonmoduleloadoption.3. Finally,addaSystem.Diagnostics.Debugger.Break()methodcalltopausethe
codeindebugger.
Ifeverythingisset,youwillseethefollowingcodeinthedisassemblywindow:
Interlocked.Increment(refcounter);
00007FFEF22B49AElearcx,[rsi+20h]
00007FFEF22B49B2lockadddwordptr[rcx],1
NotePleasenoticethelockprefixinthelastlineofthecode.ThisprefixisaninstructiontotheCPUtoperformanatomicincrementoperation.ThismeansthattheInterlockedclassisnotausualclass,butahinttotheJITcompilertogenerateaspecialcode.
www.EBooksWorld.ir
![Page 87: Mastering C# Concurrency - EBooksWorld...a print book customer, you are entitled to a discount on the eBook copy. Get in touch with us at for more details](https://reader035.vdocument.in/reader035/viewer/2022062917/5ed62e6504e9cb4adb670aed/html5/thumbnails/87.jpg)
www.EBooksWorld.ir
![Page 88: Mastering C# Concurrency - EBooksWorld...a print book customer, you are entitled to a discount on the eBook copy. Get in touch with us at for more details](https://reader035.vdocument.in/reader035/viewer/2022062917/5ed62e6504e9cb4adb670aed/html5/thumbnails/88.jpg)
Writinglock-freecodeSincewehaveaverylimitednumberofatomicoperations,itisveryhardtowritelock-freecode.Forsomecommondatastructures,suchasadoublelinkedlist,thereisnolock-freeimplementation.Besides,itisveryeasytomakeamistake,andthemainproblemisthatsuchcodecouldworkfine99.9percentofthetime,whichmakesdebuggingenormouslyconfusing.
Therefore,thebestpracticeistousestandardimplementationsofsuchalgorithms.AgoodplacetostartisbyusingconcurrentcollectionsfromtheSystem.Collections.Concurrentnamespacethatwasintroducedinthe.NETFramework4.0.WewillreviewthemindetailinChapter6,UsingConcurrentDataStructures.However,nowwewilltrytodonotasadvisedandimplementalock-freestackandalock-freequeuefromscratch.
Thecornerstoneofthelock-freecodeisthefollowingpattern:readsomedatafromthesharedstate,calculateanewvalue,andthenwritethenewvalueback,butonlyifthesharedstatewasn’tmutatedbyanyotherthreadbythattime.Thelastcheckandwriteoperationmustbeatomic,andthisiswhatweuseInterlocked.CompareExchangefor.Thisdescriptionlooksabitconfusing,butitcanbeillustratedwithquiteaneasyexample.Imaginemultiplethreadscalculatinganintegersuminparallel.Considerthefollowinglineofcode,forexample:
_total+=current;
Ifweusethissimplecode,wewouldgetraceconditionheresincethisoperationisnotatomic.TheeasiestwaytofixthisisbyusingatomicadditionwiththeInterlocked.Addmethod,buttoillustratetheCompareExchangemethodlogic,let’simplementtheadditionlikethis:
intbeforeValue,newValue;
do
{
beforeValue=_total;
newValue=beforeValue+current;
}
while(beforeValue!=Interlocked.CompareExchange(ref_total,newValue,
beforeValue))
First,wesavethe_totalvalueinthebeforeValuetemporaryvariable.Then,wecalculateanewvalueandstoreitinnewValue.Finally,we’retryingtosavenewValuein_total,butonlyif_totalremainsthesamewhenwestartedtheoperation.Ifnot,itmeansthatthe_totalvaluehasbeenchangedbyanotherthreadandwehavetorepeattheoperationwiththenewvalueof_total.
www.EBooksWorld.ir
![Page 89: Mastering C# Concurrency - EBooksWorld...a print book customer, you are entitled to a discount on the eBook copy. Get in touch with us at for more details](https://reader035.vdocument.in/reader035/viewer/2022062917/5ed62e6504e9cb4adb670aed/html5/thumbnails/89.jpg)
TheABAproblemRememberwhenwementionedthatlock-freeprogrammingisverycomplicated?Now,it’stimetoproveit.Hereisanothercasewhenaseeminglyrightconcurrentcodeworksabsolutelywrong.
Imaginethatwehavealock-freestackimplementationwiththeInterlocked.CompareExchangeatomiccompare-and-swap(CAS)operation.Let’sassumethatitcontainsthreeitems:Aontop,B,andC.Thread1callsthePopmethod;itsetstheoldheadvalueasAandthenewheadvalueasB.Howeverforsomereason,thread1getssuspendedbytheoperatingsystem.Meanwhile,thread2popsitemAfromthestackandsavesitforlateruse.Then,itpushesitemDonthestack.Afterdoingthis,itfinallypushesitemAbackontopofthestack,butthistimeA’snextitemisDandourstackcontainsfouritems:Aontop,D,B,andC.
Nowthefirstthreadcontinuestorun.Itcompareswhethertheoldheadvalueandthecurrentheadvaluearethesame,andtheyare!Therefore,thethreadwritesvalueBtotheheadofthestack.Now,thestackiscorruptedandcontainstwoitems:BonthetopandC.
Thedescribedprocesscanbeillustratedbythefollowingschema:
www.EBooksWorld.ir
![Page 90: Mastering C# Concurrency - EBooksWorld...a print book customer, you are entitled to a discount on the eBook copy. Get in touch with us at for more details](https://reader035.vdocument.in/reader035/viewer/2022062917/5ed62e6504e9cb4adb670aed/html5/thumbnails/90.jpg)
So,justhavingatomicCASoperationsisnotenough.Tomakethiscodeworkright,it’sveryimportanttomakesurethatwedonotreusereferencesinourcodeorallowthemtoescapetoourconsumers.Thus,whenwepushitemAtwice,itshouldbedifferentfromtheexistingitemsfromthestackperspective.Toachievethis,it’senoughtoallocateanewwrapperobjecteachtimesomethingisbeingpushedontothestack.
HereisaquotefromWikipediathatdescribestheABAproblemverywell:
Natalieiswaitinginhercarataredtrafficlightwithherchildren.Herchildrenstartfightingwitheachotherwhilewaiting,andsheleansbacktoscoldthem.Oncetheirfightingstops,Nataliechecksthelightagainandnoticesthatit’sstillred.However,whileshewasfocusingonherchildren,thelighthadchangedtogreen,andthenbackagain.Nataliedoesn’tthinkthelighteverchanged,butthepeoplewaitingbehindherareverymadandhonkingtheirhornsnow.
www.EBooksWorld.ir
![Page 91: Mastering C# Concurrency - EBooksWorld...a print book customer, you are entitled to a discount on the eBook copy. Get in touch with us at for more details](https://reader035.vdocument.in/reader035/viewer/2022062917/5ed62e6504e9cb4adb670aed/html5/thumbnails/91.jpg)
Thelock-freestackNow,wearereadytoimplementalock-freestackdatastructure.First,wedefineabaseabstractclassforourstackimplementation:
publicabstractclassStackBase<T>
Thenwehaveaninnerclasstodefineanitemonthestack:
privateclassItem
{
privatereadonlyT_data;
privatereadonlyItem_next;
publicItem(Tdata,Itemnext)
{
_data=data;
_next=next;
}
publicTData
{
get{return_data;}
}
publicItemNext
{
get{return_next;}
}
}
Theitemclasscontainsuserdataandareferencetothenextelementonthestack.Now,we’readdingastacktopitem:
privateItem_head;
Apropertythatindicateswhetherthestackisemptyisasfollows:
publicboolIsEmpty
{
get{return_head==null;}
}
Twoabstractmethodsthatstoreandretrieveanitemfromthestack:
publicabstractvoidPush(Tdata);
publicabstractboolTryPop(outTdata);
Nowwehaveabasefordifferentstackimplementationstocomparehowtheyperform.Westartwithalock-basedstack:
publicclassLockStack<T>:StackBase<T>
Asweremember,thelockstatementistranslatedtotheMonitorclassmethodcallsbytheC#compiler.ThemonitorclasstriestoavoidusingOS-levellocksandusesspinlockstoachieveaperformanceboostwhenalocktakesalittletime.We’regoingtoillustratethis
www.EBooksWorld.ir
![Page 92: Mastering C# Concurrency - EBooksWorld...a print book customer, you are entitled to a discount on the eBook copy. Get in touch with us at for more details](https://reader035.vdocument.in/reader035/viewer/2022062917/5ed62e6504e9cb4adb670aed/html5/thumbnails/92.jpg)
andcreateastackthatusesonlyOS-levellockswiththehelpoftheSystem.Threading.Mutexclass,whichusesthemutexsynchronizationprimitivefromtheOS.Wecreateamutexinstance:
privatereadonlyMutex_lock=newMutex();
Then,implementthePushandPopmethodsasfollows:
publicoverridevoidPush(Tdata)
{
_lock.WaitOne();
try
{
_head=newItem(data,_head);
}
finally
{
_lock.ReleaseMutex();
}
}
publicoverrideboolTryPop(outTdata)
{
_lock.WaitOne();
try
{
if(IsEmpty)
{
data=null;
returnfalse;
}
data=_head.Data;
_head=_head.Next;
returntrue;
}
finally
{
_lock.ReleaseMutex();
}
}
Thisimplementationputsathreadinablockedstateeverytimeithastowaitforthelocktobereleased.Thisistheworst-casescenario,andwe’regoingtoseethetestresultsthatprovethis.
Nowwewillimplementaconcurrentstackwithamonitorandlockstatement:
publicclassMonitorStack<T>:StackBase<T>whereT:class
{
privatereadonlyobject_lock=newobject();
publicoverridevoidPush(Tdata)
{
lock(_lock)
_head=newItem(data,_head);
}
www.EBooksWorld.ir
![Page 93: Mastering C# Concurrency - EBooksWorld...a print book customer, you are entitled to a discount on the eBook copy. Get in touch with us at for more details](https://reader035.vdocument.in/reader035/viewer/2022062917/5ed62e6504e9cb4adb670aed/html5/thumbnails/93.jpg)
publicoverrideboolTryPop(outTdata)
{
lock(_lock)
{
if(IsEmpty)
{
data=null;
returnfalse;
}
data=_head.Data;
_head=_head.Next;
returntrue;
}
}
}
Thenit’sthelock-freestackimplementation’sturn:
publicclassLockFreeStack<T>whereT:class
Noticethatwehadtoaddclassconstrainttothegenerictypeparameter.Wedothisbecausewecannotatomicallyexchangevaluesthataremorethan8bytesinsize.IfwelookatthegenericversionoftheInterlocked.CompareExchangemethod,wecanmakesurethatitstypeparameterhasthesameclassconstraint.
Let’sgettoimplementation:
publicvoidPush(Tdata)
{
Itemitem,oldHead;
do
{
oldHead=_head;
item=newItem(data,oldHead);
}while(oldHead!=Interlocked.CompareExchange(ref_head,item,
oldHead));
}
Thisimplementationisquitesimilartoalock-freeadditionexample.Webasicallydothesamething,onlyinsteadofaddition,we’restoringanewreferencetothestack’shead.
TheTryPopmethodcodeisslightlymorecomplicated:
publicboolTryPop(outTdata)
{
varoldHead=_head;
while(!IsEmpty)
{
if(oldHead==Interlocked.CompareExchange(ref_head,oldHead.Next,
oldHead))
{
data=oldHead.Data;
returntrue;
}
oldHead=_head;
}
www.EBooksWorld.ir
![Page 94: Mastering C# Concurrency - EBooksWorld...a print book customer, you are entitled to a discount on the eBook copy. Get in touch with us at for more details](https://reader035.vdocument.in/reader035/viewer/2022062917/5ed62e6504e9cb4adb670aed/html5/thumbnails/94.jpg)
data=null;
returnfalse;
}
Herewehavetonoticethatthestackcanbeempty;inthiscase,wereturnfalsetoindicatethatwefailedtoretrieveavaluefromthestack.
Also,wewouldliketocompareourcodetothestandardConcurrentStackimplementationfromSystem.Collections.Concurrent.Itispossibletouseaninterfacetoworkwithallcollectionsinthesameway,butinthiscase,itiseasiertocreateawrapperclassthatcontainsthesourcecollection:
publicclassConcurrentStackWrapper<T>:StackBase<T>
{
privatereadonlyConcurrentStack<T>_stack;
publicConcurrentStackWrapper()
{
_stack=newConcurrentStack<T>();
}
publicoverridevoidPush(Tdata)
{
_stack.Push(data);
}
publicoverrideboolTryPop(outTdata)
{
return_stack.TryPop(outdata);
}
}
Theonlyoperationleftistocomparetheperformancesofourstackimplementations:
privatestaticlongMeasure(StackBase<string>stack)
{
varthreads=Enumerable
.Range(0,_threadCount)
.Select(
n=>newThread(
()=>
{
for(varj=0;j<_iterations;j++)
{
for(vari=0;i<_iterationDepth;i++)
{
stack.Push(i.ToString());
}
stringres;
for(vari=0;i<_iterationDepth;i++)
{
stack.TryPop(outres);
}
}
}))
www.EBooksWorld.ir
![Page 95: Mastering C# Concurrency - EBooksWorld...a print book customer, you are entitled to a discount on the eBook copy. Get in touch with us at for more details](https://reader035.vdocument.in/reader035/viewer/2022062917/5ed62e6504e9cb4adb670aed/html5/thumbnails/95.jpg)
.ToArray();
varsw=Stopwatch.StartNew();
foreach(varthreadinthreads)
thread.Start();
foreach(varthreadinthreads)
thread.Join();
sw.Stop();
if(!stack.IsEmpty)
thrownewApplicationException("Stackmustbeempty!");
returnsw.ElapsedMilliseconds;
}
Werunseveralthreadsandeachofthesethreadspushesandpopsitemstothestackinparallel.Wewaitforallthethreadstocomplete,andcheckwhetherthestackisempty,whichmeansthattheprogramiscorrect.Finally,wemeasurethetimerequiredforalltheoperationstocomplete.
TheresultscanbedifferentandgreatlydependontheCPU.Thisoneisfroma3.4GHzquadcoreInteli7-3770CPU:
LockStack:6718ms
LockFreeStack:209ms
MonitorStack:154ms
ConcurrentStack:121ms
Thisoneisfromahyper-vvirtualmachinewithtwoCPUcoresrunningona2.2GHzquadcoreInteli7-4702HQCPUlaptopwithpowersavingmodeenabled:
LockStack:39497ms
LockFreeStack:388ms
MonitorStack:691ms
ConcurrentStack:419ms
Thetypicalresultsareasfollows:LockStackistheslowest,theLockFreeStackandMonitorStakimplementationsperformaboutthesame,andthestandardConcurrentStackshowsthebestresults.TheMonitorStackimplementationworkswellbecause,inthiscase,operationsunderlockareveryfast,thatis,abouttwoprocessorcycles,andinthissituation,spinwaitworksverywell.We’llgetbacktoexplainingtheseresultsindetaillaterinChapter6,UsingConcurrentDataStructures.
www.EBooksWorld.ir
![Page 96: Mastering C# Concurrency - EBooksWorld...a print book customer, you are entitled to a discount on the eBook copy. Get in touch with us at for more details](https://reader035.vdocument.in/reader035/viewer/2022062917/5ed62e6504e9cb4adb670aed/html5/thumbnails/96.jpg)
Thelock-freequeueStackandqueuearethesimplestofbasicdatastructures.Wehaveimplementedalock-freestack,andweencounteredseveraltrickyproblemsthatwehadtoresolve.Implementingalock-freeconcurrentqueueisamoresophisticatedtask,sincewenowhavetoperformseveraloperationsatonce.Forexample,whenwequeueanewitem,wemustsimultaneouslysettheoldtail’snextitemreferencetoanewitemandthenchangeatailreferencethatthenewitemisnowanewtail.Unfortunately,wecannotchangetwoobjectsasanatomicoperation.So,wemustfindawaytoproperlysynchronizeaccesstotheheadandtailwithoutlocks:
publicclassLockFreeQueue<T>
{
Wedefineasimpleclassthatisgoingtocontaindatainthequeue:
protectedclassItem
{
publicTData;
publicItemNext;
}
Wewillstorereferencestothequeue’stailandheadandinitializethembydefault:
privateItem_head;
privateItem_tail;
publicLockFreeQueue()
{
_head=newItem();
_tail=_head;
}
ThefirstchallengeistoimplementanEnqueuemethod.Whatwecandoissettail.NextintheCASoperation,butletthetailreferenceadvancelater,maybebyotherthreads.Thisguaranteesthatthelinkedlistofqueueitemswillalwaysbevalid,andifweseethatwefailedtosetanewtail,justletthisoperationstartinanotherthread:
publicvoidEnqueue(Tdata)
{
Createanewqueueitemandreservespaceforthelocalcopiesofthe_tailand_tail.Nextreferences:
Itemitem=newItem();
item.Data=data;
ItemoldTail=null;
ItemoldNext=null;
Werepeatthequeueingoperationuntilitsucceeds:
boolupdate=false;
while(!update){
Copyreferencestolocalvariablesandacquireafullfencesothatthereadandwritewww.EBooksWorld.ir
![Page 97: Mastering C# Concurrency - EBooksWorld...a print book customer, you are entitled to a discount on the eBook copy. Get in touch with us at for more details](https://reader035.vdocument.in/reader035/viewer/2022062917/5ed62e6504e9cb4adb670aed/html5/thumbnails/97.jpg)
operationswillnotbereordered.WehavetousetheNextfieldfromthelocalcopy,becausetheactual_tailitemmayhavealreadybeenchangedbetweenboththereadoperations:
oldTail=_tail;
oldNext=oldTail.Next;
Thread.MemoryBarrier();
Thetailmayremainthesameasitwasinthebeginningoftheoperation:
if(_tail==oldTail)
{
Inthiscase,thenextreferencewasnull,whichmeansthatnoonechangedthetailsincewecopiedittooldNext:
if(oldNext==null)
{
Herewecantryqueueinganitem,andthiswillbethesuccessofthewholeoperation:
update=Interlocked.CompareExchange(ref_tail.Next,item,null)==null;
}
else
{
Ifnot,itmeansthatanotherthreadisqueueinganewitemrightnow,soweshouldtrytosetthetailreferencetopointtoitsnextnode:
Interlocked.CompareExchange(ref_tail,oldNext,oldTail);
}
}
}
Herewehavesuccessfullyinsertedanewitemtotheendofthequeue,andnowwe’retryingtoupdatethetailreference.However,ifwefailitisokay,sinceanotherthreadwilleventuallydothisinitsEnqueuemethodcall:
Interlocked.CompareExchange(ref_tail,item,oldTail);
}
Themaingoalofdequeueingproperlyistocorrectlyworkinsituationswhenwehavenotyetupdatedthetailreference:
publicboolTryDequeue(outTresult)
{
Wewillcreatealoopthatfinishesifthereisnothingtodequeueorifwehavedequeuedanitemsuccessfully:
result=default(T);
ItemoldNext=null;
booladvanced=false;
while(!advanced)
{
www.EBooksWorld.ir
![Page 98: Mastering C# Concurrency - EBooksWorld...a print book customer, you are entitled to a discount on the eBook copy. Get in touch with us at for more details](https://reader035.vdocument.in/reader035/viewer/2022062917/5ed62e6504e9cb4adb670aed/html5/thumbnails/98.jpg)
Wewillmakelocalcopiesofvariablesthatareneeded:
ItemoldHead=_head;
ItemoldTail=_tail;
oldNext=oldHead.Next;
Then,wewillacquireafullfencetopreventreadandwritereordering:
Thread.MemoryBarrier();
Theremightbeacasewhentheheaditemhasnotbeenchangedyet:
if(oldHead==_head)
{
Then,wewillcheckwhetherthequeueisempty:
if(oldHead==oldTail)
{
Inthiscase,thisshouldbefalse.Ifnot,itmeansthatwehavealaggingtailandweneedtoupdateit:
if(oldNext!=null)
{
Interlocked.CompareExchange(ref_tail,oldNext,oldTail);
continue;
}
Ifwearehere,wehaveanemptyqueue:
result=default(T);
returnfalse;
}
Nowwewillgetthedequeueingitemandtrytoadvancetheheadreference:
result=oldNext.Data;
advanced=Interlocked.CompareExchange(
ref_head,oldNext,oldHead)==oldHead;
}
}
Wewillremoveanyreferencesthatcanpreventthegarbagecollectorfromdoingitsjob,andthenwewillexit:
oldNext.Data=default(T);
returntrue;
}
publicboolIsEmpty
{
get
{
return_head==_tail;
}
}
www.EBooksWorld.ir
![Page 99: Mastering C# Concurrency - EBooksWorld...a print book customer, you are entitled to a discount on the eBook copy. Get in touch with us at for more details](https://reader035.vdocument.in/reader035/viewer/2022062917/5ed62e6504e9cb4adb670aed/html5/thumbnails/99.jpg)
}
Thenwewillwritethefollowingcodetounifyaccesstoqueuesandcomparedifferentwaystosynchronizeaccesstothequeue.Towritegeneralperformancemeasurementcode,weneedtowriteaninterface:
publicinterfaceIConcurrentQueue<T>
{
voidEnqueue(Tdata);
boolTryDequeue(outTdata);
boolIsEmpty{get;}
}
BothLockFreeQueueandthestandardConcurrentQueuearealreadyimplementingthisinterface,andallweneedtodoistocreateawrapperclasslikethis:
classLockFreeQueueWrapper<T>:LockFreeQueue<T>,IConcurrentQueue<T>{}
classConcurrentQueueWrapper<T>:ConcurrentQueue<T>,IConcurrentQueue<T>
{}
Weneedamoreadvancedwrapperinthecaseofanon-thread-safeQueuecollection:
classQueueWrapper<T>:IConcurrentQueue<T>
{
privatereadonlyobject_syncRoot=newobject();
privatereadonlyQueue<T>_queue=newQueue<T>();
publicvoidEnqueue(Tdata)
{
lock(_syncRoot)
_queue.Enqueue(data);
}
publicboolTryDequeue(outTdata)
{
if(_queue.Count>0)
{
lock(_syncRoot)
{
if(_queue.Count>0)
{
data=_queue.Dequeue();
returntrue;
}
}
}
data=default(T);
returnfalse;
}
publicboolIsEmpty
{
get{return_queue.Count==0;}
}
}
www.EBooksWorld.ir
![Page 100: Mastering C# Concurrency - EBooksWorld...a print book customer, you are entitled to a discount on the eBook copy. Get in touch with us at for more details](https://reader035.vdocument.in/reader035/viewer/2022062917/5ed62e6504e9cb4adb670aed/html5/thumbnails/100.jpg)
WehaveusedadoublecheckedlockingpatterninsidetheTryDequeuemethod.Atfirstglance,itseemsthatthefirstifstatementisnotdoinganythinguseful,andwecanjustremoveit.Ifyoudoanexperimentandruntheprogramwithoutthefirstcheck,itwillrunabout50timesslower.Thegoalofthefirstcheckistoseewhetherthequeueisemptysothatalockisnotacquired;thelockandotherthreadsareallowedtoaccessthequeue.Makingalockcodeminimalisveryimportant,anditisillustratedhereverywell.
Nowweneedperformancemeasurement.Wecanwriteageneralizedcodeandprovideourdifferentqueuesinasimilarway:
privatestaticlongMeasure(IConcurrentQueue<string>queue)
{
varthreads=Enumerable
.Range(0,_writeThreads)
.Select(n=>newThread(()=>
{
for(inti=0;i<_iterations;i++)
{
queue.Enqueue(i.ToString());
Thread.SpinWait(100);
}
}))
.Concat(new[]{newThread(()=>
{
varleft=_iterations*_writeThreads;
while(left>0)
{
stringres;
if(queue.TryDequeue(outres))
left--;
}
})
})
.ToArray();
varsw=Stopwatch.StartNew();
foreach(varthreadinthreads)
thread.Start();
foreach(varthreadinthreads)
thread.Join();
sw.Stop();
if(!queue.IsEmpty)
thrownewApplicationException("Queueisnotempty!");
returnsw.ElapsedMilliseconds;
}
Thelastthingthatweneedisjustruntheprogramandtheresultsaregoingtobelikethis:
privateconstint_iterations=1000000;
privateconstint_writeThreads=8;
publicstaticvoidMain()
{
Console.WriteLine("Queue:{0}ms",Measure(newQueueWrapper<string>()));
Console.WriteLine("LockFreeQueue:{0}ms",Measure(new
LockFreeQueueWrapper<string>()));
www.EBooksWorld.ir
![Page 101: Mastering C# Concurrency - EBooksWorld...a print book customer, you are entitled to a discount on the eBook copy. Get in touch with us at for more details](https://reader035.vdocument.in/reader035/viewer/2022062917/5ed62e6504e9cb4adb670aed/html5/thumbnails/101.jpg)
Console.WriteLine("ConcurrentQueue:{0}ms",Measure(new
ConcurrentQueueWrapper<string>()));
}
Theoutputisasfollows:
Queue:3453ms
LockFreeQueue:1868ms
ConcurrentQueue:1162ms
Theseresultsshowthatourlock-freequeuehasanadvantageoverstraightforwardlocking,butthestandardConcurrentQueueperformsbetter.Itusescomplicatedwaysofstoringdata—alinkedlistofarraysegments,whichallowsustoorganizeamoreoptimalprocessofstoringandreadingdata.
www.EBooksWorld.ir
![Page 102: Mastering C# Concurrency - EBooksWorld...a print book customer, you are entitled to a discount on the eBook copy. Get in touch with us at for more details](https://reader035.vdocument.in/reader035/viewer/2022062917/5ed62e6504e9cb4adb670aed/html5/thumbnails/102.jpg)
www.EBooksWorld.ir
![Page 103: Mastering C# Concurrency - EBooksWorld...a print book customer, you are entitled to a discount on the eBook copy. Get in touch with us at for more details](https://reader035.vdocument.in/reader035/viewer/2022062917/5ed62e6504e9cb4adb670aed/html5/thumbnails/103.jpg)
SummaryInthischapter,wehavelearnedhowwecansynchronizeconcurrentaccesstoshareddatawithoutlocking.Wefoundoutwhatamemorymodelandatomicoperationareandhowthe.NETFrameworkallowsprogrammerstousethemincode.Wehavediscussedthemajorproblemsrelatedtolock-freeprogrammingandmadesurethatatomicityisnecessary,butnotenoughtomaketheconcurrentcodeworkright.Also,wehaveimplementedalock-freestackandqueueandillustratedthelock-freeapproachwithconcreteexamples.
Inthenextchapter,wewillcombineapproachesthatwehavelearnedsofarandseehowwecanstructureaconcurrentprogramtolowertheperformanceoverheadandoptimizeit,dependingonwhatexactlytheprogramdoes.
www.EBooksWorld.ir
![Page 104: Mastering C# Concurrency - EBooksWorld...a print book customer, you are entitled to a discount on the eBook copy. Get in touch with us at for more details](https://reader035.vdocument.in/reader035/viewer/2022062917/5ed62e6504e9cb4adb670aed/html5/thumbnails/104.jpg)
www.EBooksWorld.ir
![Page 105: Mastering C# Concurrency - EBooksWorld...a print book customer, you are entitled to a discount on the eBook copy. Get in touch with us at for more details](https://reader035.vdocument.in/reader035/viewer/2022062917/5ed62e6504e9cb4adb670aed/html5/thumbnails/105.jpg)
Chapter3.UnderstandingParallelismGranularityOneofthemostessentialtaskswhenwritingparallelcodeistodivideyourprogramintosubsetsthatwillruninparallelandcommunicatebetweeneachother.Sometimesthetasknaturallydividesintoseparatepieces,butusuallyitisuptoyoutochoosewhichpartstomakeparallel.Shouldweuseasmallnumberoflargetasks,manysmalltasks,ormaybelargeandsmalltasksatthesametime?
Theoreticallyspeaking,itdoesnotmatter.Incaseofanidealcomputationaldevice,itwouldhavenooverheadforcreatingaworkerthreadanddistributingworkbetweenanynumbersofthreads.However,onarealCPU,thisperformanceoverheadissignificantanditisveryimportanttotakethisintoaccount.Therightwaytosplityourprogramintoparallelpartsisthekeytowritingeffectiveandfastprograms.Inthischapter,wearegoingtoreviewthisproblemindetail.
www.EBooksWorld.ir
![Page 106: Mastering C# Concurrency - EBooksWorld...a print book customer, you are entitled to a discount on the eBook copy. Get in touch with us at for more details](https://reader035.vdocument.in/reader035/viewer/2022062917/5ed62e6504e9cb4adb670aed/html5/thumbnails/106.jpg)
ThenumberofthreadsOneoftheeasiestwaystosplityourprogramintoaparallelexecutingpartisusingthreads.However,whatisathread’scostfortheoperatingsystemandCPU?Whatnumberofthreadsisoptimal?
InWindowsandinthe32-bitmode,themaximumnumberofthreadsinyourprocessisrestrictedbythevirtualaddressspaceavailable,whichistwogigabytes.Athreadstack’ssizeisonemegabyte,sowecanhavemaximum2,048threads.Ina64-bitOSfora32-bitprocess,itshouldbe4,096.Howeverinpractice,theaddressspacewillbefragmentedandoccupiedbysomeotherdata,andthereareotherreasonswhythemaximumnumberofthreadscanbesignificantlydifferent.
Thebestwaytofindoutwhat’sgoingonistowriteacodethatchecksourassumptions.Herewewillprintthecurrentsizeofahandle,givingusawaytodetectwhetherwearein32-bitor64-bitmode.Thenthecodewillstartnewthreadsuntilwegetanyexception,anditwillprintoutthenumberofthreadsthatwewereabletostart:
Console.WriteLine(IntPtr.Size);
varcnt=0;
try
{
for(vari=0;i<int.MaxValue;i++)
{
newThread(()=>Thread.Sleep(Timeout.Infinite)).Start();
cnt++;
}
}
Catch
{
Console.WriteLine(cnt);
}
In32-bitmodeon64-bitWindows,resultscouldbelikethis:
4
1522
Whenweswitchto64-bitmode,wewillgetthefollowing:
8
71926
NotePleasebeawarethatifwerunthisin64-bitmode,theprogramwillexhaustsystemresourcesandmightcausetheOStohang!
In64-bitmode,wehavenotightaddressspacerestrictionsanymore,butthereareotherlimitedresourcessuchasoperatingsystemhandles,kernelmemoryspace,andmore.So,wedonotknowexactlyhowmanythreadsweshouldbeabletorun.However,whyarewegetting1,522threadswhileweexpectedtogetabout4,000whenwecompiledourprogramin32-bitmode?
www.EBooksWorld.ir
![Page 107: Mastering C# Concurrency - EBooksWorld...a print book customer, you are entitled to a discount on the eBook copy. Get in touch with us at for more details](https://reader035.vdocument.in/reader035/viewer/2022062917/5ed62e6504e9cb4adb670aed/html5/thumbnails/107.jpg)
Therearetworeasonsbehindthis:
Thefirstreasonisthatwhenweruna32-bitprocesson64-bitWindows,athreadwillhavea64-bitstackaswell,andtheactualstackallocationwillbe1MB+256KBofthe64-bitstack(oreven1MBontheWindowsversionspriortoWindowsVista).Thesecondreasonisthatourprocessislimitedto2GBoftheaddressspace.Ifwewanttousemore,wehavetospecifyaspecialflag,IMAGE_FILE_LARGE_ADDRESS_AWARE,forourprogram,whichissetusingthe/LARGEADDRESSAWARElinkeroption.WecannotsetthisflagdirectlyinVisualStudio,butweareabletouseatoolcalledEditBin.exe,whichisincludedinVisualStudioinstallation.
Tousethistool,justopenVisualStudioDeveloperCommandPromptandrunthefollowingcommand:
editbin/LARGEADDRESSAWAREpath\to\your\program.exe
Toswitchoffthisflag,usethissyntax:
editbin/LARGEADDRESSAWARE:NOpath\to\your\program.exe
Ifyousetthisflagfortheprecedingprogram,youwillseethatweareabletocreateabout3,200threads.Noticethatwecanusetheso-called4-gigabytetuningon32-bitWindows,andusingthisalongwiththeprecedingoption,wecanget3GBofmemoryforour32-bitprocess,whichshouldgiveusabout3,000threads.
However,doweneedtocreatethatmanythreads?Athreadisaquiteexpensiveresource,andifmorethreadsarecreated,morecorrespondingworkhastobeperformedbytheCPU.Besidesthis,moderndesktopCPUssupportonlyafewparallelthreads—from2to12atthemoment.CPUsonservershavemorecoresandcanrunmorethreads,butserver-sideconcurrencyisquitedifferentandwillbereviewedlaterindetailinChapter8,Server-SideAsynchrony.Therefore,creatingmorethreadswillnotmakeaprogrameffective,butinsteaditwillmaketheprogramslower.
Toprovethis,weneedtoexploreamorecomplicatedprogramthanjustusingThread.SpinWaittosimulateCPUload.WewouldliketoseearealcomputationaltaskthatwillinvolveeveryCPU’sblockworkingunderheavyload.Atasklikethiscanbeanimplementationofaraytracingalgorithmtorenderseveralspheres.HereisaquotefromWikipedia:
Incomputergraphics,raytracingisatechniqueforgeneratinganimagebytracingthepathoflightthroughpixelsinanimageplaneandsimulatingtheeffectsofitsencounterswithvirtualobjects.
Itisrelativelyeasytoimplement,anditcanbeeasilyscaledbecausethedifferentpartsofascenehavenoshareddataandcanberenderedindependently.Thefullcodecanbefoundinthecodesamplesofthischapter.TheactualrenderingcodeisplacedinsidetheRenderScenemethod.Itacceptsstartandendcolumnnumbersandanarrayofpixelcolors,whichwillcontaintheresultsofarenderingprocess.
www.EBooksWorld.ir
![Page 108: Mastering C# Concurrency - EBooksWorld...a print book customer, you are entitled to a discount on the eBook copy. Get in touch with us at for more details](https://reader035.vdocument.in/reader035/viewer/2022062917/5ed62e6504e9cb4adb670aed/html5/thumbnails/108.jpg)
Inthebeginning,wedefinedthealgorithmparameters.Inthesamplecode,wewillusetheimagedimensionsof1920x1920pixels:
privateconstint_width=1920;
privateconstint_height=1920;
Thismaynotfitintoyourscreen,andtoavoidcomplexity,scrollingwasnotimplementedhere.So,iftheresultantimageistoolarge,youcansimplyloweritssize.However,themeasurementsweretakenfortheinitialimagesize.
Todisplaytherenderingresults,wewillcalltheShowResultmethod.ItcreatesSystem.Drawing.Bitmapwithrenderingresults,createsthePictureBoxcontrolwiththisbitmapdata,andshowsitinWindowsFormsApplication:
privatestaticvoidShowResult(Color[,]data)
{
varbitmap=newBitmap(_width,_height,PixelFormat.Format32bppArgb);
for(vari=0;i<_width;i++)
for(varj=0;j<_height;j++)
bitmap.SetPixel(i,j,data[i,j]);
varpic=newPictureBox{
Image=bitmap,
Dock=DockStyle.Fill
};
varform=newForm{
ClientSize=newSize(_width,_height)
};
form.KeyDown+=(s,a)=>form.Close();
form.Controls.Add(pic);
Application.Run(form);
}
Then,wecanrunthiscodelikethis:
vardata=newColor[_width,_height];
RenderScene(data,0,_width);
ShowResult(data);
Torenderthescene,therearetwoloopsgoingthroughtheXandYcoordinates.Tomaketherenderingprocessparallel,wecanusetheXcoordinatetosplitthecalculationsbetweenworkerthreads,soeachthreadwillrenderitsowncolumnsset.Thenwewillincreasetheworkerthreadsnumberandrepeattheprocesstomeasureperformance:
for(varthreadCnt=1;threadCnt<=32;threadCnt++)
{
varpart=_width/threadCnt;
varthreads=Enumerable.Range(0,threadCnt).Select(
n=>{
varstartCol=n*part;
varendCol=n==threadCnt-1
?_width-(threadCnt-1)*part-1
:(n+1)*part;
returnnewThread(()=>RenderScene(data,startCol,endCol));
www.EBooksWorld.ir
![Page 109: Mastering C# Concurrency - EBooksWorld...a print book customer, you are entitled to a discount on the eBook copy. Get in touch with us at for more details](https://reader035.vdocument.in/reader035/viewer/2022062917/5ed62e6504e9cb4adb670aed/html5/thumbnails/109.jpg)
}).ToArray();
varsw=Stopwatch.StartNew();
foreach(varthreadinthreads)
thread.Start();
foreach(varthreadinthreads)
thread.Join();
sw.Stop();
Console.WriteLine("{0}threads.Rendertime{1}ms",threadCnt,
sw.ElapsedMilliseconds);
}
Thisistherenderingresult:
ThisisthedependencybetweenthenumberofworkerthreadsandtheoverallperformanceonaCorei72600KCPUanda64-bitOS:
www.EBooksWorld.ir
![Page 110: Mastering C# Concurrency - EBooksWorld...a print book customer, you are entitled to a discount on the eBook copy. Get in touch with us at for more details](https://reader035.vdocument.in/reader035/viewer/2022062917/5ed62e6504e9cb4adb670aed/html5/thumbnails/110.jpg)
Thischartshowsthreemainstages.Thefirststageisasignificantperformanceimprovementwhenweincreasethethreadnumberuptofour.AnIntelCorei72600KCPUhasfourphysicalcores,andloadingallthecoresgivesusalmostlinearscalability.Thenwecanhaveasmootherperformancechangewhilegoingfromfourtoeightthreads.ThisisduetothefactthatthisCPUsupportshyperthreadingtechnology.Thehyper-threadedcoresareimplementedwithasecondsetofhardwareregistersinthesamecore,buttheyusethesamecomputepipeline.Withoutgoingintotoomuchdetail,wecansaythatthisoftencanbeveryefficientandcanperformalmostliketwophysicalCPUcores.Inthisexample,wecanseethatthehyperthreadingtechnologyallowstheprogramtorunfaster.
Thelaststageiswhenweincreasethethreadsnumberfrom8to32.Thelinegoesslightlyupandthismeansthatwedonotgainanyadvantageandonlyloseperformance.TheCPUcannotperformfasterbecausewehavealreadyputthemaximumworkloadonitandcreatingmorethreadsonlyleadstocreatingmoreworkforrunningthethreadsandnotcalculations.
Thus,themosteffectiveoptionisusingasmanythreadsascoresyourCPUhasandasmanylogicalcoresyouroperatingsystemsupports.16threadsisacommonnumberthatwillbeenoughformostofthepresentandnearfuturedesktopCPUs.TheotheroptionistousetheEnvironment.ProcessorCountvariabletoknowduringruntimehowmanycorestheconcreteCPUhas.
NotePleasenoticethatingeneralyoushouldnotusethreadsdirectly.Thereareotherpossibilitiesofrunningtasksinparallel,andyoushouldusethreadsonlywhenyouare
www.EBooksWorld.ir
![Page 111: Mastering C# Concurrency - EBooksWorld...a print book customer, you are entitled to a discount on the eBook copy. Get in touch with us at for more details](https://reader035.vdocument.in/reader035/viewer/2022062917/5ed62e6504e9cb4adb670aed/html5/thumbnails/111.jpg)
100%awareoftheadvantagesanddisadvantagesofotherapproaches.We’llreviewsomeofthemlaterinthisbook.
www.EBooksWorld.ir
![Page 112: Mastering C# Concurrency - EBooksWorld...a print book customer, you are entitled to a discount on the eBook copy. Get in touch with us at for more details](https://reader035.vdocument.in/reader035/viewer/2022062917/5ed62e6504e9cb4adb670aed/html5/thumbnails/112.jpg)
www.EBooksWorld.ir
![Page 113: Mastering C# Concurrency - EBooksWorld...a print book customer, you are entitled to a discount on the eBook copy. Get in touch with us at for more details](https://reader035.vdocument.in/reader035/viewer/2022062917/5ed62e6504e9cb4adb670aed/html5/thumbnails/113.jpg)
UsingthethreadpoolAsalreadymentioned,creatingathreadisquiteanexpensiveoperation.Inadditiontothis,creatingmoreandmorethreadsisnotefficient.Tomakeasynchronousoperationseasier,inCommonLanguageRuntimethereisathreadpool,whichisrepresentedbytheSystem.Threading.Threadpoolclass.Insteadofcreatingathreadeverytimeweneedone,weaskthethreadpoolforaworkerthread.Ifithasathreadavailable,athreadpoolreturnsittous.Whenitsjobisdone,itgoesbackintothethreadpoolinasuspendedstateuntilitisneededagain.
Therearetwotypesofthreadsinsidethethreadpool:workerthreadsandinput/outputthreads.I/OthreadsareusedforasynchronousI/Oprocessingandwearenotgoingtoreviewthemhere.Let’sconcentrateonworkerthreadsinstead.ThisiswhatMSDNstatesaboutthreadpoolanditslimits:
Thereisonethreadpoolperprocess.
Beginningwiththe.NETFramework4,thedefaultsizeofthethreadpoolforaprocessdependsonseveralfactors,suchasthesizeofthevirtualaddressspace.AprocesscancalltheGetMaxThreadsmethodtodeterminethenumberofthreads.
ThenumberofthreadsinthethreadpoolcanbechangedbyusingtheSetMaxThreadsmethod.
Eachthreadusesthedefaultstacksizeandrunsatthedefaultpriority.
Ifwetrytoacquiremoreworkerthreadsthanthethreadpool’slimit,thesubsequentrequestswillbequeuedandwillwaituntilaworkerthreadbecomesavailable.So,wecannothavemorethreadpoolworkerthreadsthanitslimitatatime.
Inpractice,thethreadpoolimplementationisverycomplicatedandreliesonempiricassumptions.Also,ithasbeenchangedwithnew.NETFrameworkversions,anditispossiblethatitwillbechangedinfuture,soweshouldnotrelyonspecificimplementationdetails.
However,thecommonlogicissimple;thethreadpoolmaintainsasmallnumberofworkerthreadsandcreatesmorethreadswhenneededuntilthelimitisreached.Toseehowthisworks,wecanwriteacodethatwillcreatethreadpoolworkerthreadsandseehowmanythreadsarebeingallocatedatatime:
for(vari=0;i<_threadCount;i++)
ThreadPool.QueueUserWorkItem(
s=>
{
Interlocked.Increment(ref_runCount);
Thread.Sleep(5000);
Interlocked.Decrement(ref_runCount);
});
Thread.Sleep(1000);
while(_runCount>0)
{
Console.WriteLine(_runCount);
www.EBooksWorld.ir
![Page 114: Mastering C# Concurrency - EBooksWorld...a print book customer, you are entitled to a discount on the eBook copy. Get in touch with us at for more details](https://reader035.vdocument.in/reader035/viewer/2022062917/5ed62e6504e9cb4adb670aed/html5/thumbnails/114.jpg)
Thread.Sleep(100);
}
Weenqueueanumberofworkerthreads,andeachofthemincrementsathreadcounter,thenwaitsfor5seconds,andthendecrementsthecounter,signalingthatitsworkisfinished.Inthemainthread,weprintoutthiscountertoseehowtheworkerthreadsarebeingallocated.
Forthe.NETFramework4.5andaspecifichardware,thiscodeshowsthatatfirstwealmostimmediatelyhavenineworkerthreads,thenthecountergrowsslowlyuntil35-40,andthenitgoesbackto0.Thus,usingthethreadpoolwithalargenumberoftasksallowsustoeffectivelyloadtheCPUandabstractfromtheactualthreadsusagespecifics.
Thereisonemoreworthwhilethingtomentionaboutthethreadpoolthatcanbegoodorbadindifferentscenarios.Thereisonethreadpoolperprocess,andeverylibraryandframeworkthatyouusecanpotentiallyworkwiththethreadpool,sosomeoftheworkerthreadscanbealreadybusywiththird-partycodetasks.So,ifsomelibraryisnotwellwrittenandoccupiesmanyworkerthreadsorblocksthemwithlong-runningoperations,thenyourprogramwillnotbeabletoeffectivelyloadtheCPU.Also,thiscanbecausedbyathird-partycodethatworkswithinput/outputoperationincorrectly,whichleadstoperformancedegradationaswell.
SoifyourprogramusesthethreadpoolforcomputationtasksandtheCPUisnotfullyloaded,itisworthcheckinghowmanyworkerthreadsarethereandwhatexactlytheyaredoing.Specifically,thisisextremelyimportantforserver-sideconcurrency,whereserverframeworksusuallysharethethreadpoolwithyourcode.
www.EBooksWorld.ir
![Page 115: Mastering C# Concurrency - EBooksWorld...a print book customer, you are entitled to a discount on the eBook copy. Get in touch with us at for more details](https://reader035.vdocument.in/reader035/viewer/2022062917/5ed62e6504e9cb4adb670aed/html5/thumbnails/115.jpg)
www.EBooksWorld.ir
![Page 116: Mastering C# Concurrency - EBooksWorld...a print book customer, you are entitled to a discount on the eBook copy. Get in touch with us at for more details](https://reader035.vdocument.in/reader035/viewer/2022062917/5ed62e6504e9cb4adb670aed/html5/thumbnails/116.jpg)
UnderstandinggranularityWhenthereisonecommoncomputationaltaskinsideyourapplication,itisquiteobvioushowtomakeitruninparallel.ThemosteffectivesolutionwouldbetodividethetasksinpartsandrunthesepartsoneachavailableCPUcore.Sincethenumberofpartswillnotbelarge,therewillnotbeanysignificantperformanceoverhead.Thiswayofdividingyourcodeintoparallelrunningtasksiscalledcoarse-grained:
Thereisoneproblemwiththecoarse-grainedapproach.Thelargetaskscanrunatsignificantlydifferenttimes,andthenatthesetimes,someoftheCPUcoreswillnotbeusedtohelpcomputetheothertasks.OnemorepossibilityisthatthesetaskscanblocktheCPUcoreswhilewaitingforsomesignalsfromotherthreadsorinput/outputoperationtocomplete.Inthiscase,theCPUtimewouldbewasted.
Tobemoreeffective,wewillhavetosplitthesetasksintomoreparts.Ifthenumberofpartswillbeless,thenwewillstillhavetheproblemofsometasksrunningmuchfasterthanothersdoandsomeofCPUcoreswillbeunavailableforfurthercomputations.So,wehavetosplitthetasksintomanysmallpiecesuntilwecansaythatblockingonetaskisnotimportantbecausetheCPUcanswitchtoanothertaskatonce.Thisapproachiscalledfine-grained:
www.EBooksWorld.ir
![Page 117: Mastering C# Concurrency - EBooksWorld...a print book customer, you are entitled to a discount on the eBook copy. Get in touch with us at for more details](https://reader035.vdocument.in/reader035/viewer/2022062917/5ed62e6504e9cb4adb670aed/html5/thumbnails/117.jpg)
Howcanweimplementsuchaprogram?Wehavetodivideourcomputationintoverysmalltasksandminimizetheoverheadforeachtasksincetherewillbemanyofthem,andwedonotwanttowasteCPUtimetosupportthesetasks’infrastructureinsteadofdoingcomputation.Thenwehavetofindawaytorunthesetaskseffectively.
Itisverycomplicatedtowriteageneralalgorithmtodividemanydifferentcomputationtasksintoseveralworkerthreads.Fortunately,suchframeworksalreadyexistandoneofthemisincludedinthe.NETFramework.ItiscalledTaskParallelLibrary(TPL).WewilldiscussTPLindetailinChapter4,TaskParallelLibraryinDepth.
Now,wewilluseTPLtowriteafine-grainedparallelprogram.WesimulatethatthetasksaredifferentbyrunningSpinWaitwithdifferentnumberofcycles.Thenwesplitourtasksintodifferently-sizedpiecesandcalculatethenumberofiterationspermillisecondthatwewereabletorun.
Thesamplecodewillbeasfollows:
varrandom=newRandom();
vartaskSizes=
Enumerable
.Range(0,_totalSize)
.Select(n=>random.NextDouble())
.ToArray();
for(varworkSize=256;workSize>0;workSize-=4)
{
www.EBooksWorld.ir
![Page 118: Mastering C# Concurrency - EBooksWorld...a print book customer, you are entitled to a discount on the eBook copy. Get in touch with us at for more details](https://reader035.vdocument.in/reader035/viewer/2022062917/5ed62e6504e9cb4adb670aed/html5/thumbnails/118.jpg)
vartotal=0;
vartasks=newList<Task>();
vari=0;
while(total<_totalSize)
{
varcurrentSize=(int)(taskSizes[i]*workSize)+1;
tasks.Add(
newTask(
()=>
{
Thread.SpinWait(currentSize*_sizeElementaryDelay);
}));
i++;
total+=currentSize;
}
varsw=Stopwatch.StartNew();
foreach(vartaskintasks)
task.Start();
Task.WaitAll(tasks.ToArray());
sw.Stop();
Console.WriteLine(
"Worksize{0},
Taskcount{1},Effectiveness{2:####}works/ms",
workSize,tasks.Count,
((double)total*_sizeElementaryDelay)/sw.ElapsedMilliseconds);
}
}
ThefundamentalentityinTPListheSystem.Threading.Taskclass,whichrepresentsabasictaskthathastoberun.Tocomparetheperformanceoflargetasksversussmalltasks,wewillgothroughthefollowingprocess:
Weprepareanarrayofrandomtasksizestocreateauniquesetoftasksforeachtimeweruntheprogram.Thenwesplitthetotalworkintoasmallnumberoflargetasks,runthemeasurement,andthenrepeatthewholeprocessonceagainbysplittingtheworkintosmallertasksandmakingthetotalnumberoftaskslarger.EachmeasurementinvolvesstartingStopwatch,runningalltasks,waitingforallthetaskstocompletewiththeTask.WaitAllmethod,andthenmeasuringhowmuchtimeittooktocompleteallthetasks.
Hereissamplechartillustratingtheresultsofrunningthiscode:
www.EBooksWorld.ir
![Page 119: Mastering C# Concurrency - EBooksWorld...a print book customer, you are entitled to a discount on the eBook copy. Get in touch with us at for more details](https://reader035.vdocument.in/reader035/viewer/2022062917/5ed62e6504e9cb4adb670aed/html5/thumbnails/119.jpg)
Thischartshowsthatwhenwereducetasksize,weincreaseperformanceuntilsomepoint.ThenthetasksizebecomessmallenoughtoachievefullCPUworkload.Makingtaskssmallerbecomesineffectiveduetoanoveralltaskoverheadincrease.
Thiswasasynthetictest.Inpractice,everythingwilldependonaprogram’snature.Ifitispossibletovarythetasksizeforyourprogram,andifperformanceiscrucial,youcanrunseveraltestsandfindoutthebestparametersexperimentally.
www.EBooksWorld.ir
![Page 120: Mastering C# Concurrency - EBooksWorld...a print book customer, you are entitled to a discount on the eBook copy. Get in touch with us at for more details](https://reader035.vdocument.in/reader035/viewer/2022062917/5ed62e6504e9cb4adb670aed/html5/thumbnails/120.jpg)
www.EBooksWorld.ir
![Page 121: Mastering C# Concurrency - EBooksWorld...a print book customer, you are entitled to a discount on the eBook copy. Get in touch with us at for more details](https://reader035.vdocument.in/reader035/viewer/2022062917/5ed62e6504e9cb4adb670aed/html5/thumbnails/121.jpg)
Choosingthecoarse-grainedorfine-grainedapproachFine-grainedparallelismgranularityallowsustorunheterogeneouscomputationaltaskseffectively.Besidesthis,thefine-grainedapproachmakesthesplittingofyourprogramintotaskseasier,especiallyifthesetasksarerelatedtoeachotherand,forexample,lattertasksusesomecomputationresultsofformertasks.However,wewillhavetotradeoffsomeperformance,sincetheCPUhastobeusedtomanageallthesetasksaswell.
Tofindouthowfine-grainedgranularitycanaffectperformanceforarealtask,let’simplementaraytracingalgorithmusingTPLandcompareittotheresultsthatwegotinthebeginningusinganoptimalnumberofthreads.Toimplementthefine-grainedprogramversion,wewilljustcreateataskforeachimagecolumnandstartitimmediately.Theimplementationcodeisasfollows:
vartasks=newList<Task>();
varfineSw=Stopwatch.StartNew();
for(vari=0;i<_width;i++)
{
varcol=i;//Createseparatevariableforclosure
tasks.Add(Task.Factory.StartNew(()=>RenderScene(data,col,col)));
}
Task.WaitAll(tasks.ToArray());
fineSw.Stop();
Console.WriteLine("Finegrained{0}ms",fineSw.ElapsedMilliseconds);
Executingthiscodeincoarse-grainedmodetakesabout150millisecondsonthespecifichardware.Afine-grainedmodetakesabout160milliseconds.Atfirstglance,thedifferenceisinsignificant.However,itisstillnoticeable,evenafterknowingthattheTPLcodeisverywelloptimized.So,ifperformanceisveryimportant,itispossibletotryimplementingparallelismgranularitybyyourself.Howeverbeforethis,youmustbeabsolutelysurethatthebottleneckisgranularityandtheresultsofthetestsconductedapprovethis.
Ifnot,justuseTPLandfine-grainedapproach,whichiseasytocodeandstillprovidesgoodperformance.
www.EBooksWorld.ir
![Page 122: Mastering C# Concurrency - EBooksWorld...a print book customer, you are entitled to a discount on the eBook copy. Get in touch with us at for more details](https://reader035.vdocument.in/reader035/viewer/2022062917/5ed62e6504e9cb4adb670aed/html5/thumbnails/122.jpg)
www.EBooksWorld.ir
![Page 123: Mastering C# Concurrency - EBooksWorld...a print book customer, you are entitled to a discount on the eBook copy. Get in touch with us at for more details](https://reader035.vdocument.in/reader035/viewer/2022062917/5ed62e6504e9cb4adb670aed/html5/thumbnails/123.jpg)
SummaryInthischapter,wehavereviewedaproblemofparallelcomputationsgranularity.Wehavetrieddifferentwaystosplitourprogramintoconcurrentlyexecutingpiecesandsawtheperformanceimpactineachcase.Also,we’veimplementedarealcomputationtaskofrenderingsphereswitharaytracingalgorithmandlearnedtoparallelizeitwiththreadsandTaskParallelLibrary.
Inthenextchapter,wewillcontinuetolearnTaskParallelLibrary.Weshallreviewthisframeworkindetailandclarifyeveryaspectofusingitincludinghowthetasksarebeingrun,howwecombinetaskstogether,andhowtohandleexceptionsandtimeouts.
www.EBooksWorld.ir
![Page 124: Mastering C# Concurrency - EBooksWorld...a print book customer, you are entitled to a discount on the eBook copy. Get in touch with us at for more details](https://reader035.vdocument.in/reader035/viewer/2022062917/5ed62e6504e9cb4adb670aed/html5/thumbnails/124.jpg)
www.EBooksWorld.ir
![Page 125: Mastering C# Concurrency - EBooksWorld...a print book customer, you are entitled to a discount on the eBook copy. Get in touch with us at for more details](https://reader035.vdocument.in/reader035/viewer/2022062917/5ed62e6504e9cb4adb670aed/html5/thumbnails/125.jpg)
Chapter4.TaskParallelLibraryinDepthInthepreviouschapter,wehavealreadyusedTPLtosimplifythewritingofsomefine-grainedparallelcode.Thecodelookedquiteclear;however,TPLisaquitecomplicatedframeworkwithahighlevelofabstraction,anditdeservesadetailedreview.
Mostcodesamplesthatwehaveseensofarwerequitesimpleintermsofcomposition.Wetookacomputationalproblem,splititintoseveralindependentparts,andranthesepartsondifferentworkerthreads.Whenallthepartsarecompleted,wegettheirresultsandcombinethemintoafinalcalculationresult.However,mostreal-worldprogramsusuallyhaveacomplexstructure.Weneedtogetinputdata,andthenthereareprogramstagesthatdependoneachother;tocontinuethecalculations,wehavetogetresultsfrompreviousstages.Thesestagescantakedifferentdurationstocompleteandrequiredifferentapproachesforparallelization.
Itispossibletowritethislogicbasedonworkerthreadsandsynchronizationprimitives.However,withmanypartsanddependencies,suchcodewillbecometoolargeandverbose.Tomaketheprogrammingeasier,wecantakeadvantageofdifferentparallelprogrammingmodelimplementationsthatabstractthreadsandsynchronizationmechanicsandoffersomekindofahigher-levelAPIthatismucheasiertouse.ThisistheparallelprogrammingmodeldefinitionfromWikipedia:
Incomputersoftware,aparallelprogrammingmodelisamodelforwritingparallelprogramswhichcanbecompiledandexecuted.Thevalueofaprogrammingmodelcanbejudgedonitsgenerality:howwellarangeofdifferentproblemscanbeexpressedforavarietyofdifferentarchitectures,anditsperformance:howefficientlytheyexecute.Theimplementationofaprogrammingmodelcantakeseveralformssuchaslibrariesinvokedfromtraditionalsequentiallanguages,languageextensions,orcompletenewexecutionmodels.
Onesuchmodelistask-basedparallelism.Itsmainconceptisatask,whichisjustapieceofsynchronouslyexecutingcode.Ifonetaskdependsonanothertask’sresult,wecanprovidesuchinformationtotheframework.Thefinalpartisthetaskscheduler.Itknowsaboutthecurrentenvironmentandcanexecutetasksonanoptimalnumberofthreads,takingintoaccounttheinformationaboutdependenciesbetweenthetasks.Theprogramcodetransformsintodefiningtasksandtheirdependencies,whichismuchcleanerthanrawthreadsorthreadpoolusage.
Letusreconsideracodesamplefromthepreviouschapter:
for(vari=0;i<_width;i++)
{
varcol=i;//Createseparatevariableforclosure
tasks.Add(Task.Factory.StartNew(()=>RenderScene(data,col,col)));
}
Task.WaitAll(tasks.ToArray());
Here,wehaveusedalooptoiteratethroughallthecolumnsofourscene,andthenwesplitcalculationstocreateaseparatetaskforeachcolumn.Tocreatesuchtasks,weusetheSystem.Threading.Taskclass.TheStartNewmethodcreatesanewTaskinstanceand
www.EBooksWorld.ir
![Page 126: Mastering C# Concurrency - EBooksWorld...a print book customer, you are entitled to a discount on the eBook copy. Get in touch with us at for more details](https://reader035.vdocument.in/reader035/viewer/2022062917/5ed62e6504e9cb4adb670aed/html5/thumbnails/126.jpg)
startsthetaskatonce.Whenwehavecompletedcreatingallthetasks,wewillusetheTask.WaitAllstaticmethodtowaituntilallthetaskscompletetheirjobs.
www.EBooksWorld.ir
![Page 127: Mastering C# Concurrency - EBooksWorld...a print book customer, you are entitled to a discount on the eBook copy. Get in touch with us at for more details](https://reader035.vdocument.in/reader035/viewer/2022062917/5ed62e6504e9cb4adb670aed/html5/thumbnails/127.jpg)
TaskcompositionLet’sconsiderasituationwhere,beforerunningatask(let’scallthetask,taskB),wewillneedaresultfromthecalculationofaprevioustask,taskA.Suchdependencybetweentasksisusuallycalledfutureorpromise.Thismeansthat,whenweruntaskA,wedonotknowitsresultbeforethecalculationsarecomplete.Sowestate(makeapromise)that,atsomepointinthefuture,wewillruntaskBassoonaswegettheresultfromtaskA.
Whydoweneedtodeclaredependenciesinaspecificway?Wecanalwayscreatedependenttasksasfollows:
vartaskA=newTask<string>(
()=>
{
Console.WriteLine("TaskAstarted");
Thread.Sleep(1000);
Console.WriteLine("TaskAcomplete");
return"A";
});
taskA.Start();
vartaskB=newTask(
()=>
{
Console.WriteLine("TaskBstarted");
Console.WriteLine("TaskAresultis{0}",taskA.Result);
});
taskB.Start();
taskB.Wait();
Theresultisthis:
TaskAstarted
TaskBstarted
TaskAcomplete
TaskAresultisA
First,wecreateanewtaskAinstanceanduseathreadpoolworkerthreadtoexecutethecodeinsidethistask.
NoteBydefault,TaskParallelLibraryuses.NETasthethreadpooltoruntaskcode.However,itispossibletouseotherwaystoruntasks,andthepartofTPLthatisresponsibleforrunningtasksiscalledthetaskscheduler.Wewillreviewtaskschedulerslaterinthischapter.
TheoutputdisplaysTaskAstartedandsimulatessomecalculationsusingtheThread.Sleepmethod.Atthesametime,wewillcreateanewtaskBinstance,whichusesanotherthreadpoolworkerthreadtorun.ItoutputsTaskBstartedtotheconsoleandthenblocksuntiltaskAcompletes.Then,taskAsignalsitscompletionbyprintingTaskAcompleteandreturnsthe“A”stringasaresult.TaskBgetsasignalthattaskAiscompletedandprintstheresultasTaskAresultisA.
So,itseemsthatwehavesuccessfullycreatedtwodependenttasks.Unfortunately,this
www.EBooksWorld.ir
![Page 128: Mastering C# Concurrency - EBooksWorld...a print book customer, you are entitled to a discount on the eBook copy. Get in touch with us at for more details](https://reader035.vdocument.in/reader035/viewer/2022062917/5ed62e6504e9cb4adb670aed/html5/thumbnails/128.jpg)
codewillbequiteineffectiveandhardtomaintain.Imaginethatweneedmoredependencies.Thiscodewillinturncreatemanytasksthatwilluseothertasks’results,andtounderstanddependencies,areaderwillhavetoanalyzeeachtask’scode.Besidesthis,whentaskAruns,taskBblocksthethreadpoolthread.Itmeansthatwehavejustwastedoneworkerthreadthatisdoingnothingandcannotbeusedtoservesomeotherjob.Ifwecreatemanytasks,wewillsoontakeoveralltheworkerthreadsfromathreadpool,andthisisaverybadpracticethatleadstoscalabilityandperformanceproblems.
Nevertheless,itisobviousthatthereisnosenseinrunningtasksAandBinparallel,sinceBneedsAtocomplete.Torunthiscodesynchronously,wecanmergethecodefromAandB,butthiswouldbreakupprogramlogicandleadusbacktothecoarse-grainedapproach.
Anotherwayistoanalyzedependenciesbetweentasksandusethreadpoolworkerthreadsmoreefficiently.Forexample,donotscheduletaskBcodeexecutionuntiltaskAcodefinishesandreturnsitsresult.Allweneedtodoistodeclareadependencybetweentasksexplicitly,soTPLwillknowwhattaskstorunfirstandwhattodelay.ThisisexactlywhattheTask.ContiueWithmethoddoes.Weusethismethodonaninitialtask,andthisreturnsanothertask(usuallycalledacontinuationtask)thatwillbeexecutedaftertheformertaskcompletes:
vartaskA=newTask<string>(
()=>
{
Console.WriteLine("TaskAstarted");
Thread.Sleep(1000);
Console.WriteLine("TaskAcomplete");
return"A";
});
taskA
.ContinueWith(
task=>
{
Console.WriteLine("TaskBstarted");
Console.WriteLine("TaskAresultis{0}",task.Result);
});
taskA.Start();
taskA.Wait();
WecreatedataskAinstancesimilartothepreviousexample.However,insteadofcreatinganewtask,weusedtheContinueWithmethodonthetaskAinstancethatallowsustoprovideacodethatwillberunwhentaskAcompletes.WehaveaccesstothetaskAinstanceviathetaskparameterofthelambdaexpression.NowtheTPLtaskschedulerwillplaceacontinuationtaskcodeonathreadpoolonlyaftertaskArunstocompletion.
Theresultofthiscodewillbeasfollows:
TaskAstarted
TaskAcomplete
TaskBstarted
TaskAresultisA
Noticethattheorderofmessagesisdifferentthanthepreviousresult.NowtaskBis
www.EBooksWorld.ir
![Page 129: Mastering C# Concurrency - EBooksWorld...a print book customer, you are entitled to a discount on the eBook copy. Get in touch with us at for more details](https://reader035.vdocument.in/reader035/viewer/2022062917/5ed62e6504e9cb4adb670aed/html5/thumbnails/129.jpg)
startedaftertaskAcompletes.
Thiscanbeadisadvantageifthelattertaskperformssomeworkthatcanberuninparallel.Inthiscase,runningtaskBafterAwillbeinefficient,sinceitisactuallyasynchronouscodeexecution.However,TPLisabouttaskcompositionanditsimplymeansthatwecansplittaskBintotwotasks;onewillruninparallelwithtaskAandtheotherwillbeplacedinacontinuationtask:
vartaskA=newTask<string>(
()=>
{
Console.WriteLine("TaskAstarted");
Thread.Sleep(1000);
Console.WriteLine("TaskAcomplete");
return"A";
});
taskA.Start();
vartaskB1=newTask(()=>Console.WriteLine("TaskB1started"));
taskB1.Start();
taskA.ContinueWith(tsk=>Console.WriteLine("TaskAresultis{0}",
tsk.Result));
taskA.Wait();
Ifwerunthiscode,theresultswillshowthattaskAandB1runinparallel;B1canevenberunbeforeA,sinceitdoesnotreallymatterintermsofprogramlogicinwhatorderindependenttasksarescheduledtorun.
Therearemorecomplicatedwaysofcomposingtasks.Forexample,whenataskneedsresultsfrommultipletasks,theContinueWithmethodallowsustofollowonlyonetask,andweneedtaskB2togettheresultsfromAandB1:
However,thereisaTaskFactoryclassthatcanbeaccessedthroughtheTask.Factorystaticproperty.Itcontainsmanyusefulthingstocreateandscheduletasks,butwhatweneednowisitsContinueWhenAllmethod.
Theimplementationofthemultipledependencyschemaisasfollows:
vartaskA=newTask<string>(
www.EBooksWorld.ir
![Page 130: Mastering C# Concurrency - EBooksWorld...a print book customer, you are entitled to a discount on the eBook copy. Get in touch with us at for more details](https://reader035.vdocument.in/reader035/viewer/2022062917/5ed62e6504e9cb4adb670aed/html5/thumbnails/130.jpg)
()=>
{
Console.WriteLine("TaskAstarted");
Thread.Sleep(1000);
Console.WriteLine("TaskAcomplete");
return"A";
});
taskA.Start();
vartaskB1=newTask<string>(
()=>
{
Console.WriteLine("TaskB1started");
Thread.Sleep(500);
Console.WriteLine("TaskB1complete");
return"B";
});
taskB1.Start();
Task
.Factory
.ContinueWhenAll(
new[]{taskA,taskB1},
tasks=>Console.WriteLine("TaskAresultis{0},TaskBresultis
{1}",tasks[0].Result,tasks[1].Result));
taskA.Wait();
TheContinueWhenAllmethodacceptsanarrayoftasksasitsfirstparameterandalambdaexpressionasthesecond.Thelambdaexpressiontasksparameteristhetasksarraythatwehavejustprovided.Insteadofusingthis,itispossibletocreateaclosureandaccessthetaskB1andtaskAvariablesinthelambdabody,butthiswouldcreateunnecessarydependenciesinthecode,whichisgenerallyabadpractice.
NoteThisisoftenreferredtoascodecoupling.Whenthecodehasmanydependencies,itiscalledhighcoupling;inthiscase,thecodeishardtomaintain,sinceanychangecanaffecttheotherparts.Lowcouplingmeansthatthispartofthecodedoesnotdependonotherparts,soitcanbechangedandmaintainedeasilywithoutbreakingtheothercode,andotherchangeswillmostlikelynotbreakthispartoftheprogram.
Theresultswillbeasfollows:
TaskAstarted
TaskB1started
TaskB1complete
TaskAcomplete
TaskAresultisA,TaskBresultisB
ThisshowsthattasksAandB1runindependentlyinparallel,andthefinalcodegetstheresultsfromboththetasks.Wesuccessfullydescribeddependenciesinadeclarativeway,andtheTPLinfrastructureensuredthecorrectnessoftheexecutionorderandprogramlogic.
www.EBooksWorld.ir
![Page 131: Mastering C# Concurrency - EBooksWorld...a print book customer, you are entitled to a discount on the eBook copy. Get in touch with us at for more details](https://reader035.vdocument.in/reader035/viewer/2022062917/5ed62e6504e9cb4adb670aed/html5/thumbnails/131.jpg)
ItisworthmentioninganotherTaskFactoryclassmethod,theContinueWhenAnymethod,whichisquitesimilartoContinueWhenAll.Itcreatesataskthatstartswhenanyoftheprovidedtasksinthearraycomplete.Thisisusefulforhavingseveralalternativewaystoachievetheresult,andweusetheonethatcompletesfasterthantheothers.
www.EBooksWorld.ir
![Page 132: Mastering C# Concurrency - EBooksWorld...a print book customer, you are entitled to a discount on the eBook copy. Get in touch with us at for more details](https://reader035.vdocument.in/reader035/viewer/2022062917/5ed62e6504e9cb4adb670aed/html5/thumbnails/132.jpg)
www.EBooksWorld.ir
![Page 133: Mastering C# Concurrency - EBooksWorld...a print book customer, you are entitled to a discount on the eBook copy. Get in touch with us at for more details](https://reader035.vdocument.in/reader035/viewer/2022062917/5ed62e6504e9cb4adb670aed/html5/thumbnails/133.jpg)
TaskshierarchyWementionedbeforethatthetaskschedulerneedsexplicitlydefineddependenciesbetweentaskstorunthemeffectivelyandinthecorrectorder.However,besidesthis,thereisawaytoachieveimplicitdependencydefinition;whenwecreateonetaskinsideanother,aspecialparent-childdependencyiscreatedforthesetasks.Bydefault,thisdoesnotaffecthowthesetaskswillbeexecuted,butthereisawaytomakethisdependencyreallyimportant.
WecancreateataskwiththeTaskFactory.CreateNewmethodbyprovidingaspecialTaskCreationOptions.AttachedToParentparameter.Thischangestheusualtaskbehavior,andtheimportantdifferencesareasfollows:
Theparenttaskwillnotcompleteuntileverychildtaskcompletes.Ifthecasechildtaskscauseanyexceptions,theywillbetranslatedtotheparenttask.Theparenttaskstatusdependsonitschildtasks.Ifanychildtaskfails,theparenttaskwillhavetheTaskStatus.Faultedstatusaswell.
Toillustratethis,wecancomparethebehaviorofthedefaulttaskandthetaskattachedtotheparent.Here,wewillcreateachildtaskwithoutspecifyingthetaskcreationoptions:
Task
.Factory
.StartNew(
()=>
{
Console.WriteLine("Parentstarted");
Task
.Factory
.StartNew(
()=>
{
Console.WriteLine("Childstarted");
Thread.Sleep(100);
Console.WriteLine("Childcomplete");
});
})
.Wait();
Console.WriteLine("Parentcomplete");
Asaresultwegetthefollowing:
Parentstarted
Childstarted
Parentcomplete
Itisimportantthattheparenttaskhascompletedbeforethechildtask,andsincewewaitedonlyfortheparenttask,themainthreadexitedandthechildtaskdidnotcompleteatall.
NowweaddtheAttachedToParentoptioninthesamecode,changingonlythechildtaskasfollows:
Task
www.EBooksWorld.ir
![Page 134: Mastering C# Concurrency - EBooksWorld...a print book customer, you are entitled to a discount on the eBook copy. Get in touch with us at for more details](https://reader035.vdocument.in/reader035/viewer/2022062917/5ed62e6504e9cb4adb670aed/html5/thumbnails/134.jpg)
.Factory
.StartNew(
()=>
{
Console.WriteLine("Childstarted");
Thread.Sleep(100);
Console.WriteLine("Childcomplete");
},
TaskCreationOptions.AttachedToParent);
Runthisagaintogetthefollowing:
Parentstarted
Childstarted
Childcomplete
Parentcomplete
Here,wecanseethattheparenttaskwaitsuntilthechildtaskfinishesandonlythenchangesitsstatustoTaskStatus.RanToCompletion.
www.EBooksWorld.ir
![Page 135: Mastering C# Concurrency - EBooksWorld...a print book customer, you are entitled to a discount on the eBook copy. Get in touch with us at for more details](https://reader035.vdocument.in/reader035/viewer/2022062917/5ed62e6504e9cb4adb670aed/html5/thumbnails/135.jpg)
www.EBooksWorld.ir
![Page 136: Mastering C# Concurrency - EBooksWorld...a print book customer, you are entitled to a discount on the eBook copy. Get in touch with us at for more details](https://reader035.vdocument.in/reader035/viewer/2022062917/5ed62e6504e9cb4adb670aed/html5/thumbnails/136.jpg)
AwaitingtaskcompletionTherearedifferentwaystowaituntiltheTPLtaskcompletes.Inthepreviouscode,weusedtheTask.Waitmethod.Thismethodblocksthecurrentthreaduntilthistaskcompletes.Ifthetaskgivesaresult,thesameeffectcanbeachievedwhentheTask.Resultinstancepropertyisqueried.Thisisabasicwaytocoordinatetasksintheprogram.
Whenweneededtowaitformultipletasks,weusedtheTask.WaitAllstaticmethod.Ifwekeepasidetheoptimizationandexceptionhandlingcode,thismethodwillbeimplementedusingthefollowinglogic:
varwaitedOnTaskList=newList<Task>(tasks.Length);
for(inti=tasks.Length-1;i>=0;i--)
{
Tasktask=tasks[i];
if(!taskIsCompleted)
waitedOnTaskList.Add(task);
}
if(waitedOnTaskList!=null)
{
WaitHandle[]waitHandles=newWaitHandle[waitedOnTaskList.Count];
for(vari=0;i<waitHandles.Length;i++)
waitHandles[i]=waitedOnTaskList[i].CompletedEvent.WaitHandle;
WaitAll(waitHandles);
}
WehavedefinedalistoftasksthatarenotcompletedyetandthenattachedanarrayofhandlestotheOS-specificobjectsthatcanbeusedtowaitforeachtasktobecompleted.Thenwewaitontheseobjectsuntilallunderlyingtasksarecompleted.
Asinthepreviouscase,alongwithWaitAll,theTasktypedefinestheWaitAnystaticmethod.Itwaitsuntilanytaskinthearrayiscompleted.Itcanbeusedtotracktheprogressoftaskcompletionortochoosethefastestwaytogetresultsfromtheseveralalternatives.
www.EBooksWorld.ir
![Page 137: Mastering C# Concurrency - EBooksWorld...a print book customer, you are entitled to a discount on the eBook copy. Get in touch with us at for more details](https://reader035.vdocument.in/reader035/viewer/2022062917/5ed62e6504e9cb4adb670aed/html5/thumbnails/137.jpg)
www.EBooksWorld.ir
![Page 138: Mastering C# Concurrency - EBooksWorld...a print book customer, you are entitled to a discount on the eBook copy. Get in touch with us at for more details](https://reader035.vdocument.in/reader035/viewer/2022062917/5ed62e6504e9cb4adb670aed/html5/thumbnails/138.jpg)
TaskcancellationAtaskrepresentsacommonasynchronousoperation.Thismeansthatwedon’tknowwhenitcompletes.Sometimes,itisclearthatwedonotneedthistaskanymore.Forexample,iftheoperationtakestoolongtocomplete,ortheuserclicksontheCancelbutton.Inthiscase,weneedtostopthetask.
Oneofthelower-levelwaystostopathreadisbycallingitsAbortmethod.Beforegoingon,Iwouldliketoemphasizetheimportanceofnotusingthis.
NoteNevereveruseThread.Abort!
Thread.AbortraisesaveryspecialexceptioncalledThreadAbortExceptiononathreadthatisbeingaborted.Thisexceptioncanhappenatmoreorlessanypointinyourprogramandcannotbestoppedbytheusualexceptionhandling.Wecanwriteacodewithcatchblockandthecodeinsidethisblockwillwork,butassoonasthecatchblockends,thesameexceptionwillberaisedagain.But—surprise—ifwecalltheThread.CurrentThread.ResetAbortmethodinsidethecatchblock,thethreadabortrequestwillbecanceled.ThismeansthatcallingThread.Abortdoesnotguaranteethatthethreadwillbeactuallyaborted.
Anotheraspectofusingthismethodisthatitaffectsonlythemanagedcode.Ifyourthreadiswaitingforunmanagedcodetocomplete,whichisalmosteveryI/Ooperation,thethreadwillnotbeaborteduntilthisoperationends.Iftheoperationnevercompletes,thecodewillneverreturnandyourprogramwillhang.
Also,duetothe.NETCLRconstructingtypealgorithmspecifics,thisexceptioncanbreakyourprogram.Ifthereisanexceptioninsideastaticconstructorofsometype,thisexceptiongetscached,andallfurtherattemptstousethistypewillleadtothrowingthisexception.SoifwecallThread.Abortandraisethisexceptionwhilethetargetthreadwasexecutinganystaticconstructor,wewillgetThreadAbortExceptionwhenanythreadtriestoaccessthetypethatfailedtobecreatedonthepreviousthread.
Ifthisisnotenough,thereisonemoreillustrationofhowevilthisexceptionis.Imaginethecodethatisusuallywrittenforworkingwithfiles:
using(FileStreamfs=File.Open(fileName,...))
{
...dostuffwithdatafile…
}
Theprecedingcodeistheshorterversionofthefollowingcode:
FileStreamfs=File.Open(fileName,...);
try
{
...dostuffwithdatafile…
}
finally
{
IDisposabledisposable=fs;
www.EBooksWorld.ir
![Page 139: Mastering C# Concurrency - EBooksWorld...a print book customer, you are entitled to a discount on the eBook copy. Get in touch with us at for more details](https://reader035.vdocument.in/reader035/viewer/2022062917/5ed62e6504e9cb4adb670aed/html5/thumbnails/139.jpg)
disposable.Dispose();
}
SinceThreadAbortExceptioncanemergeatanypoint,itcanhappeninsidethefinallyblock.Ifithappensthere,thecodeinthisblockwillnotruntocompletionandthefilewillremainopened.Inthiscase,theFileStreamclassimplementsadisposablepatternandislikelytobeclosedwhileitsfinalizermethodiscalledwhengarbagecollectionoccurs.However,itisclearthatleavingthefileopenforanundefinedtimeisnotagoodthingandothercodeisnotalwayscorrectlywritten.
Therefore,Thread.Abortmustbeavoidedinallcircumstances.Insteadofusingthis,weshouldwritethecodewhilebeingawareofthecancellationpossibility.Itisimportantthatthiscancellationmustnotdependonanyconcretewaysofrunningtheoperationitself,sinceTPLabstractsawaytaskexecutionmechanics,allowingustowritecustomtaskschedulersandusethemwithstandardTPLcode.
Fortunately,the.NETFrameworkcontainsaCancellationAPI,andthisiswhatweshouldusetoimplementcancellationinourcode.TPLusesthisAPIaswell,whichmakesiteasiertowritecancellationcodeforTPL-basedprograms.
TheCancellationAPIisbasedontwomaintypes—theSystem.Threading.CancellationTokenstructureandtheSystem.Threading.CancellationTokenSourceclass.Thecancellationtokencontainsmethodsandpropertiesthatwecanusetohandlethecancellationrequest,andthecancellationtokensourceallowsustocreatecancellationtokensandinitiatecancellationrequests.
Atypicalsituationiswhenwehavetwopartsofacode.Thefirstpartisthecodethatcreates,combines,andrunstasks.ThiscodecaninteractwiththeprogramUIandhandlessituationswhenweneedtocancelsomeoftherunningtasks.Usually,thispartisresponsibleforcreatingCancellationTokenSource,constructingcancellationtokeninstances,andprovidingthemtoeachtaskthatcanbecancelled.Then,whenthecancellationprocessisbeinginitiated,wecalltheCancelorCancelAftermethodsoneachcancellationtokenneeded.
Thesecondpartofcodelivesinsidetasksandusescancellationtokeninstancestogetcancellationsignals.Thereareseveralcommonapproachestoimplementingthecancellationitself.Theyarecoveredinthefollowingsections.
www.EBooksWorld.ir
![Page 140: Mastering C# Concurrency - EBooksWorld...a print book customer, you are entitled to a discount on the eBook copy. Get in touch with us at for more details](https://reader035.vdocument.in/reader035/viewer/2022062917/5ed62e6504e9cb4adb670aed/html5/thumbnails/140.jpg)
CheckingaflagIfthecodeinsideataskisquiteeasy,forexample,itisaloopwithshortiterations,thentheeasiestwaytostoptheoperationistochecksomeflagvariableinsidethisloopandexitiftheflagisset.
Thefirstpartofthecodecreatesatask,providesitwithacancellationtoken,andtheninitiatesacancellationprocess.Finally,wemeasurethetimeofthetaskcancellationprocessasfollows:
privatestaticvoidRunTest(Action<CancellationToken>action,stringname)
{
varcancelSource=newCancellationTokenSource();
varcancelToken=cancelSource.Token;
vartask=Task
.Factory
.StartNew(()=>action(cancelToken),cancelToken);
//Waitforstartingtask
while(task.Status!=TaskStatus.Running){}
varsw=Stopwatch.StartNew();
cancelSource.Cancel();
while(!task.IsCompleted){}
sw.Stop();
Console.WriteLine("{0}taskcancelledin{1}ms",name,
sw.ElapsedMilliseconds);
}
Noticethatweareprovidingacancellationtokennotonlytoourtask,butalsototheStartNewmethodaswell.ThereasonforthisisthatTPLisawareofcancellationtokensaswellandcancancelthetaskevenifithasnotstartedyetandourcodeisnotabletohandlecancellation.
Also,weusealoopinsteadofcallingtheWaitmethod.TheWaitmethodhasanoverloadacceptingthecancellationtokeninstance.IfwecalltheCancelmethodfromthetoken,theWaitmethodwillreturntheexecutionatonce.Thisisabuilt-incancellationmechanisminTPL,butweneedcustomcancellationnow,soweemulatewaitingwithtaskstatuscheckinginsidetheloop.First,wewaituntilthetaskactuallystarts,andthenweinitiatecancellationandwaituntilthetaskcompletes.
Finally,westopthetimerandprintouttheresults.
Thecodeforthetaskrunsaninfiniteloop,waits,andcheckswhetheracancellationisrequested:
RunTest(tok=>
{
while(true)
{
Thread.Sleep(100);
if(tok.IsCancellationRequested)
break;
}
www.EBooksWorld.ir
![Page 141: Mastering C# Concurrency - EBooksWorld...a print book customer, you are entitled to a discount on the eBook copy. Get in touch with us at for more details](https://reader035.vdocument.in/reader035/viewer/2022062917/5ed62e6504e9cb4adb670aed/html5/thumbnails/141.jpg)
},"CheckFlag");
Theresultcanbelikethis:
CheckFlagtaskgotcancelledin103ms
Thismeansthatthecancellationhappenedinthefirstloopiterationassoonasthetaskcodecheckedtheflag.
www.EBooksWorld.ir
![Page 142: Mastering C# Concurrency - EBooksWorld...a print book customer, you are entitled to a discount on the eBook copy. Get in touch with us at for more details](https://reader035.vdocument.in/reader035/viewer/2022062917/5ed62e6504e9cb4adb670aed/html5/thumbnails/142.jpg)
ThrowinganexceptionIfthecodeinsidethetaskiscomplicated,itisdifficulttochecktheflagineverypartofthecode.Therecanbemanyloopsinsidemanymethods,andifwegetresultsfromamethodthatcanbecancelled,weneedtoprovideadditionalinformationtodistinguishwhetherthismethodwascancelledorsuccessfullyrantocompletion.Inthiscase,itismucheasiertouseanothercancellationtechnique—throwingaspecialcancellationexception.
IfweusetheCancellationToken.ThrowIfCancellationRequestedmethodonourtoken,thenitwillthrowOperationCanceledExceptionwhencancellationisrequested.Thisexceptionwillstopcodeexecutioninsidethetask,bubbleuptotheTPLinfrastructurethatwillhandleit,andsettaskstatustoTaskState.Canceled.
Insteadofcheckingtheflag,weinstructthetokentoraiseOperationCanceledExceptionwhenreceivingacancellationrequest:
RunTest(tok=>
{
while(true)
{
Thread.Sleep(100);
tok.ThrowIfCancellationRequested();
}
},"ThrowException");
Thechangesareminimal,andtheresultshouldbethesame:
ThrowExceptiontaskgotcancelledin109ms
AssoonaswegettotheThrowIfCancellationRequestedmethod,thecalloperationgetscancelledwithanexception.
www.EBooksWorld.ir
![Page 143: Mastering C# Concurrency - EBooksWorld...a print book customer, you are entitled to a discount on the eBook copy. Get in touch with us at for more details](https://reader035.vdocument.in/reader035/viewer/2022062917/5ed62e6504e9cb4adb670aed/html5/thumbnails/143.jpg)
UsingOSwaitobjectswithWaitHandleThenextoptionisusefulwhenthecodeinsideataskiswaitingonanOSsynchronizationprimitiveforasignificanttime.Here,wecanuseCancellationToken.WaitHandletoincludeinthewaitingprocessandreactimmediatelywhencancellationisrequested.
Thisisusuallycombinedwithoneofthepreviouslydescribedtechniques—wejuststopwaitingandproceedwiththecancellation.
Thisishowitlooks:
RunTest(tok=>
{
varevt=newManualResetEvent(false);
while(true)
{
WaitHandle.WaitAny(new[]{evt,tok.WaitHandle},100);
tok.ThrowIfCancellationRequested();
}
},"WaitHandle");
Inthisexample,wehavecreatedaManualResetEventinstancetowaitonitinsteadofusingThread.Sleep.However,wehaveusedWaitHandle.WaitAnytoincludethecancellationtokeninthewaitingprocess.So,herewewaitfortheeventortokentobesignaledusingthe100mstimeoutvalueandthencontinuerunningtheloop.
Nowtheresultshouldbedifferentasfollows:
WaitHandletaskgotcancelledin0ms
Sinceweareabletoproceedwiththecancellationassoonasthetokengetssignaled,ithappensalmostimmediately.
www.EBooksWorld.ir
![Page 144: Mastering C# Concurrency - EBooksWorld...a print book customer, you are entitled to a discount on the eBook copy. Get in touch with us at for more details](https://reader035.vdocument.in/reader035/viewer/2022062917/5ed62e6504e9cb4adb670aed/html5/thumbnails/144.jpg)
CancellationusingcallbacksItisgoodwhenyoucontrolallthecode,anditispossibletochangeeverypieceofthecodetoimplementcancellationproperly.However,themostcommonsituationiswhenyouusesomeexternalcodeinsideyourtaskandyoudonotcontrolthiscode.Imagineifthisisconnectedviaaslownetworktosomeserverandthisfetchesdata.YoupresstheCancelbutton,buttheoperationwillnotcompleteuntilitfinishestheI/Ooperation.Thisisnotaverygooduserexperienceandcanbeakeyreasonfortheusertochooseadifferentsoftware.
Ofcourse,wecanwritesimilarcodefromscratch.However,usuallywedonotneedto,sincealmosteverythird-partycodesuchasthisprovidessomething,suchastheCloseorDisposemethods,allowingustointerruptcommunicationandreleaseallocatedresources.Theproblemisthatthesemethodscanbeverydifferentineverythird-partyframework.
Fortunately,thecancellationAPIprovidesuswithapossibilitytoregisteranycancellationcodeasacallbackandrunthiscallbackassoonasacancellationisrequested.Toillustratethisapproach,wecanwriteaclient/serverapplicationandimplementacallbackcancellation.
Theserverpartisrelativelysimple.Wejustneedtoallowinboundconnectionandsimulateaslowresponse:
constintport=8083;
newThread(()=>
{
varlistener=newTcpListener(IPAddress.Any,port);
listener.Start();
while(true)
using(varclient=listener.AcceptTcpClient())
using(varstream=client.GetStream())
using(varwriter=newStreamWriter(stream))
{
Thread.Sleep(100);
writer.WriteLine("OK");
}
}){IsBackground=true}
.Start();
Thisserverwilllistenforincomingconnectionsonport8083;whentheconnectionisestablished,itwaitsfor100msandrespondswithanOKstring.
Insideourtask,wearegoingtoconnecttothisserverviatheTcpClientclassandthencanceltheconnectionassoonaspossible:
RunTest(tok=>
{
while(true)
{
using(varclient=newTcpClient())
{
client.Connect("localhost",port);
using(varstream=client.GetStream())
using(varreader=newStreamReader(stream))
www.EBooksWorld.ir
![Page 145: Mastering C# Concurrency - EBooksWorld...a print book customer, you are entitled to a discount on the eBook copy. Get in touch with us at for more details](https://reader035.vdocument.in/reader035/viewer/2022062917/5ed62e6504e9cb4adb670aed/html5/thumbnails/145.jpg)
Console.WriteLine(reader.ReadLine());
}
tok.ThrowIfCancellationRequested();
}
},"Callback");
Thissampleprintsthefollowingresult:
OK
Callbacktaskgotcancelledin109ms
Thiscodeconnectstotheserverandwaitsfortheservertorespond;onlyaftergettingtheresponsedoweproceedwiththecancellation.
Accordingtothedocumentation,theTcpClientclassincludestheClosemethod.ThismethodinterruptsworkandclosestheTCPconnectionifithasbeenalreadyopened.Allweneedtodoistocallthismethodwhenacancellationisrequested:
RunTest(tok=>
{
while(true)
{
using(varclient=newTcpClient())
using(tok.Register(client.Close))
{
client.Connect("localhost",port);
using(varstream=client.GetStream())
using(varreader=newStreamReader(stream))
Console.WriteLine(reader.ReadLine());
}
tok.ThrowIfCancellationRequested();
}
},"Callback");
Thedifferenceisjustaddingasinglelineofcode.WecalltheCancellationToken.RegistermethodthatacceptsthecallbackthatwillbecalledinthecaseofcancellationandreturnstheCancellationTokenRegistrationstructure.ItimplementsIDisposableandcallingtheDisposemethodonitwillderegisterthecallback,soitwillnotbecalledifthecancellationhappensafterwards.
Sointhesamplecode,wewouldliketorunclient.Closewhenthecancellationhappensbutonlyinsidetheinnerusingblock.Ifthecancellationhappenssomewhereelse,wedonotneedtorunthiscallback.Asaresult,wewillgetsomethinglikethis:
Callbacktaskgotcancelledin3ms
Nowitisclearthatwedonotwaitfortheservertorespondandcanceltheoperationalmostimmediately.WemanagedtomaketheusershappywithoutrewritingTcpClientfromscratchwiththehelpofthecancellationAPI.
www.EBooksWorld.ir
![Page 146: Mastering C# Concurrency - EBooksWorld...a print book customer, you are entitled to a discount on the eBook copy. Get in touch with us at for more details](https://reader035.vdocument.in/reader035/viewer/2022062917/5ed62e6504e9cb4adb670aed/html5/thumbnails/146.jpg)
www.EBooksWorld.ir
![Page 147: Mastering C# Concurrency - EBooksWorld...a print book customer, you are entitled to a discount on the eBook copy. Get in touch with us at for more details](https://reader035.vdocument.in/reader035/viewer/2022062917/5ed62e6504e9cb4adb670aed/html5/thumbnails/147.jpg)
Latencyandthecoarse-grainedapproachwithTPLRawperformance,orthenumberofcalculationspersecondthatourprogramisabletoperform,isnotalwaysamostimportantgoaltoachieve.Sometimesitisevenmoreimportanttostayresponsiveandinteractwiththeuserasfastaspossible.Unfortunately,itisnoteasytoachieveboththeseadvantagesatthesametime;therearesituationswhenweneedtochooseourprimarygoal.
Tosimulatesuchasituation,let’screateacombinationofcoarse-grainedcomputationaltasksthattakesalongtimetocompleteandrunsinthebackground,andanumberofshort-livedtasksrepresentinguserinteraction.Wewouldliketheseshorttaskstorunasfastaspossiblewithlowlatency.Nowwewriteacodetotesthowtheselong-runningtaskscanaffectlatency:
for(varlongThreadCount=0;longThreadCount<24;longThreadCount++)
{
//Createcoarsegrainedtasks
varlongThreads=newList<Task>();
for(vari=0;i<longThreadCount;i++)
longThreads.Add(
Task.Factory.StartNew(
()=>Thread.Sleep(1000)));
//Measurelatency
varsw=Stopwatch.StartNew();
for(vari=0;i<_measureCount;i++)
Task
.Factory
.StartNew(()=>Thread.SpinWait(100))
.Wait();
sw.Stop();
Console.WriteLine("Longrunningthreads{0}.Averagelatency{1:0.###}
ms",longThreadCount,(double)sw.ElapsedMilliseconds/_measureCount);
Task.WaitAll(longThreads.ToArray());
}
Wehavecreatedupto24longrunningthreadsinsidetheloop,andineachiteration,wemeasuredupanaveragelatencyofrunningashorttask.Finally,wewaitforalltaskstocompleteandprintoutresults.Thisishowtheresultdatalooksonachart:
www.EBooksWorld.ir
![Page 148: Mastering C# Concurrency - EBooksWorld...a print book customer, you are entitled to a discount on the eBook copy. Get in touch with us at for more details](https://reader035.vdocument.in/reader035/viewer/2022062917/5ed62e6504e9cb4adb670aed/html5/thumbnails/148.jpg)
Wecanseethatwehaveaverylowlatencyuntileightlongrunningtasks,andthenitdramaticallyincreasesupto4-5times.Thereasonis,asusual,complex,butthemainreasonisthattheCPUinthiscasesupportsuptoeightsimultaneouslyrunningthreads.Whilelong-runningtasksoccupiedfewerthreadsthanthislimit,theremainingthreadscanbeusedtoexecuteshort-livedtasks.Assoonastherearenofreethreadsremaining,shorttaskshavetocompeteforthreadpoolworkerthreadsandshareCPUtimewiththelong-runningtasks,andthustheshorttasksbecomemuchslower.
Tomakeshorttasksfasteragain,wecanisolatelongtasksfromthethreadpoolthatrunstheshorttasks.Iftheshorttaskshavepriorityingettingresources,thentheywillrunfaster,andthelong-runningtaskswillrunabitslower,buttheshort-tasklatencywillbemuchbetter.
TPLhasanoptiontospecifythatataskislong-runningandshouldbetreatedinaspecialway:
Task.Factory.StartNew(
()=>Thread.Sleep(1000),
TaskCreationOptions.LongRunning)
In.NET4.5,thedefaulttaskschedulerrunssuchtasksonseparatethreadsthatarenotthreadpoolthreads.ThisiswhatthereferenceimplementationoftheThreadPoolTaskSchedulermethodofQueueTasklookslike:
protectedinternaloverridevoidQueueTask(Tasktask)
{
if((task.Options&TaskCreationOptions.LongRunning)!=
TaskCreationOptions.None)
{
www.EBooksWorld.ir
![Page 149: Mastering C# Concurrency - EBooksWorld...a print book customer, you are entitled to a discount on the eBook copy. Get in touch with us at for more details](https://reader035.vdocument.in/reader035/viewer/2022062917/5ed62e6504e9cb4adb670aed/html5/thumbnails/149.jpg)
newThread(s_longRunningThreadWork){IsBackground=true
}.Start(task);
}
else
{
boolforceGlobal=(task.Options&TaskCreationOptions.PreferFairness)
!=TaskCreationOptions.None;
ThreadPool.UnsafeQueueCustomWorkItem(task,forceGlobal);
}
}
However,ingeneral,wedonotknowhowsuchtaskswillbetreated,andthewayofrunningsuchtasksistotallyuptothecurrenttaskschedulerimplementation.
Addingnewresultstothechartgivesusthis:
Itseemsthatwesuccessfullyresolvedlatencyissue.Ofcourse,thelong-runningtaskswillbeslightlyslower,butthisiswhatwewantedtoachieve.
www.EBooksWorld.ir
![Page 150: Mastering C# Concurrency - EBooksWorld...a print book customer, you are entitled to a discount on the eBook copy. Get in touch with us at for more details](https://reader035.vdocument.in/reader035/viewer/2022062917/5ed62e6504e9cb4adb670aed/html5/thumbnails/150.jpg)
www.EBooksWorld.ir
![Page 151: Mastering C# Concurrency - EBooksWorld...a print book customer, you are entitled to a discount on the eBook copy. Get in touch with us at for more details](https://reader035.vdocument.in/reader035/viewer/2022062917/5ed62e6504e9cb4adb670aed/html5/thumbnails/151.jpg)
ExceptionhandlingAnotherimportantaspectofTPLisworkingwithexceptions.Justasthenormalcodethatwewritecangenerateanexception,socanthecodeinsideaTPLtask.Sinceeverytaskhasitsownstack,wecannotworkwithexceptionsintheusualway.TPLhasseveraloptionsthatallowustoworkwithexceptionsinaparallelprogram.
Theeasiestoptionistocheckthetaskstatus.Ifanexceptionhasbeenraisedinsidethetask,itwillhavetheStatuspropertysettoTaskStatus.Faulted.TheexceptionwillbeavailablethroughtheTask.Exceptionproperty:
vartask=Task.Factory.StartNew(()=>
{
thrownewApplicationException("Testexception");
});
while(!task.IsCompleted){}
Console.WriteLine("Status={0}",task.Status);
Console.WriteLine(task.Exception);
Thiscodeprintsthefollowing:
Status=Faulted
System.AggregateException:Oneormoreerrorsoccurred.--->
System.ApplicationException:Testexception
...
TheoriginalexceptionthathasbeenthrowninthecodebecamewrappedinanAggregateExceptioninstance.Thereasonisthattherecanbemanyexceptionsfromchildtasksthatruninparallel.Intheaggregateexceptioninstance,thereistheInnerExceptionspropertythatwillcontainallthewrappedexceptions.
Towaitforthetaskcompletion,wehaveusedaloopinsteadoftheTask.Waitmethod.Whenataskcompleteswithanexception,thismethodwillrethrowtheexceptiononthethreadthathascalledWait.Ifwereplacethewhileloopwiththetask.Wait()methodcallandrunthecodeagain,wewillseeanunhandledexception:
UnhandledException:System.AggregateException:Oneormoreerrors
occurred.--->System.ApplicationException:Testexception
...
ThesamebehaviorwillhappenwhenweusetheTask.ResultpropertyortheTask.WaitAll/WaitAnystaticmethods.
Whenreviewingparent-childrelationsbetweentasks,wehavestatedthat,ifwecreateachildtaskwithTaskCreationOptions.AttachedToParent,thenitsexceptionswillautomaticallybepropagatedtotheparenttask.Tochecktheexceptionbehavior,wecanquicklycreatetwonestedtasksandthrowanexceptionfromthechildtask:
Task.Factory.StartNew(()=>
{
Task.Factory.StartNew(()=>
www.EBooksWorld.ir
![Page 152: Mastering C# Concurrency - EBooksWorld...a print book customer, you are entitled to a discount on the eBook copy. Get in touch with us at for more details](https://reader035.vdocument.in/reader035/viewer/2022062917/5ed62e6504e9cb4adb670aed/html5/thumbnails/152.jpg)
{
thrownewApplicationException("Testexception");
},TaskCreationOptions.AttachedToParent);
})
.Wait();
Thiswillprintthefollowing:
UnhandledException:System.AggregateException:Oneormoreerrors
occurred.--->System.AggregateException:Oneormoreerrorsoccurred.--->
System.ApplicationException:Testexception
...
Asweexpected,theparenttaskcompletedwiththeexceptionthatbubbledfromitschildtask.However,nowwehaveanaggregateexceptionthatcontainsanotheraggregateexception,whichinturncontainstheinitialexceptionfromthechildtask.Theexceptionhierarchyrepeatsthetaskrelationship,whichisnotalwaysagoodthing.
Wemayputthepreviouscodeinatryblockandwriteacatchblocktoprinttheinnerexceptionsasfollows:
catch(AggregateExceptionae)
{
foreach(Exceptioneinae.InnerExceptions)
{
Console.WriteLine("{0}:{1}",e.GetType(),e.Message);
}
}
Theresultsoftheprecedingcodecanbesurprising:
System.AggregateException:Oneormoreerrorsoccurred.
Sinceitisahierarchy,weneedtocheckinnerexceptionsinsideeachaggregateexceptionthatweget.Sincetheaggregateexceptionisonlyacontainerforarealexception,weactuallyneedtocollectonlytheotherexceptions.Fortunately,thereisawaytoflattentheexceptionhierarchyintoasimplecollectionofinitialexceptions.Tocheckthis,let’screateacomplextaskstructureandseewhatisinsidethetop-levelexception:
vart=Task.Factory.StartNew(()=>
{
Task.Factory.StartNew(
()=>
{
Task.Factory.StartNew(
()=>
{
thrownewApplicationException("Andweneedtogodeeper");
},TaskCreationOptions.AttachedToParent);
thrownewApplicationException("Testexception");
},TaskCreationOptions.AttachedToParent);
Task.Factory.StartNew(()=>
{
thrownewApplicationException("Testsiblingexception");
www.EBooksWorld.ir
![Page 153: Mastering C# Concurrency - EBooksWorld...a print book customer, you are entitled to a discount on the eBook copy. Get in touch with us at for more details](https://reader035.vdocument.in/reader035/viewer/2022062917/5ed62e6504e9cb4adb670aed/html5/thumbnails/153.jpg)
},
TaskCreationOptions.AttachedToParent);
});
try
{
t.Wait();
}
catch(AggregateExceptionae)
{
foreach(Exceptioneinae.Flatten().InnerExceptions)
{
Console.WriteLine("{0}:{1}",e.GetType(),e.Message);
}
}
Asaresult,wewillgetalistofalltheinitialexceptions:
System.ApplicationException:Testsiblingexception
System.ApplicationException:Testexception
System.ApplicationException:Andweneedtogodeeper
Oneofthecancellationoptionsthatwehavereviewedsofarwasthrowingaspecialkindofexception,OperationCanceledException.TPLtreatsthisexceptioninaspecialway.ThetaskstatuswillbeTaskStatus.CanceledinsteadofFaulted,andtheExceptionpropertywillbeempty:
varcancelSource=newCancellationTokenSource();
vartoken=cancelSource.Token;
vartask=
Task
.Factory
.StartNew(
()=>
{
while(true)
token.ThrowIfCancellationRequested();
},
token);
while(task.Status!=TaskStatus.Running){}
cancelSource.Cancel();
while(!task.IsCompleted){}
Console.WriteLine("Status={0},IsCanceled={1}",task.Status,
task.IsCanceled);
Console.WriteLine(task.Exception);
Theresultshowsthatacancellationexceptioninthiscaseisbeingtreateddifferently:
Status=Canceled,IsCanceled=True
NotePleasenoticethatifwedonotpassatokeninstanceasthelastparameteroftheStartNewmethod,thecancellationexceptionwillbetreatedlikearegularexception.
www.EBooksWorld.ir
![Page 154: Mastering C# Concurrency - EBooksWorld...a print book customer, you are entitled to a discount on the eBook copy. Get in touch with us at for more details](https://reader035.vdocument.in/reader035/viewer/2022062917/5ed62e6504e9cb4adb670aed/html5/thumbnails/154.jpg)
www.EBooksWorld.ir
![Page 155: Mastering C# Concurrency - EBooksWorld...a print book customer, you are entitled to a discount on the eBook copy. Get in touch with us at for more details](https://reader035.vdocument.in/reader035/viewer/2022062917/5ed62e6504e9cb4adb670aed/html5/thumbnails/155.jpg)
UsingtheParallelclassTPLprovidesareachAPItocomposeaparallelprogram.However,itisquiteverbose,andifwewriteasimplecode,thereareeasierwaytoparallelizeit.Forcommontaskssuchasrunningsomecodeinparallelandparallelizingtheforandforeachloops,thereisaParallelclassthatprovidesasimpleandeasytouseAPI.
www.EBooksWorld.ir
![Page 156: Mastering C# Concurrency - EBooksWorld...a print book customer, you are entitled to a discount on the eBook copy. Get in touch with us at for more details](https://reader035.vdocument.in/reader035/viewer/2022062917/5ed62e6504e9cb4adb670aed/html5/thumbnails/156.jpg)
Parallel.InvokeThismethodexecutesactionsinparalleliftheCPUhasmultiplecoresandsupportsmultiplethreads.IftheCPUhasonlyonecore,actionswillbeexecutedsynchronously.Thismethodblocksthecallingthreaduntilalltheactionsarecompleted:
Parallel.Invoke(
()=>Console.WriteLine("Action1"),
()=>
{
Thread.SpinWait(10000);
Console.WriteLine("Action2");
},
()=>Console.WriteLine("Action3"));
Console.WriteLine("End");
Afterrunningtheprecedinglinesofcode,wegetthefollowingoutput:
Action1
Action3
Action2
End
WecanprovidetheParallelOptionsclassinstancetothismethodtoconfigureadditionaloptionssuchaslimitingtheparallelismdegree,specifyingacancellationtoken,andusingaspecificimplementationofthetaskschedulertoruntasksonit.
Thestraightforwardimplementationofthismethodwillbeasfollows:
vartasks=newList<Task>();
foreach(varactioninactions)
{
tasks.Add(Task.Factory.StartNew(action));
}
Task.WaitAll(tasks.ToArray());
However,therealimplementation,besidescancellation,correctnesschecks,andexceptionhandling,isstillverydifferent.Thisisduetocodeperformanceoptimization.Theusualtaskscheduleriswrittenassumingthatwedonotknowhowmanytaskswearegoingtorun.Inthisspecificcase,thisisadefinedvalue.IfitislessthanorequaltoSMALL_ACTIONCOUNT_LIMIT(thatis10inthecurrent.NETFrameworkversion4.5),thenthealgorithmissimilartoourimplementation.
Inthecaseofmoretasks,itbecomesmorecomplicated.First,wecreateanemptyspecialtaskcalledreplicabletask.Thistaskistreatedinaspecialwaybyataskscheduler.Theimplementationcodeisasfollows:
varactionIndex=0;
varrootTask=
newReplicableTask(
()=>
{
intmyIndex;
www.EBooksWorld.ir
![Page 157: Mastering C# Concurrency - EBooksWorld...a print book customer, you are entitled to a discount on the eBook copy. Get in touch with us at for more details](https://reader035.vdocument.in/reader035/viewer/2022062917/5ed62e6504e9cb4adb670aed/html5/thumbnails/157.jpg)
while((myIndex=InterLocked.Increment(refactionIndex))<=
actions.Length)
body(myIndex-1);
});
rootTask.RunSynchronously();
rootTask.Wait();
Here,wehavetheactionIndexlocalvariablethatisusedbythetaskcodeinsidethelambdaexpression.Thiscreatesaclosure,andtheC#compilergeneratesahelperclassinstanceandputstheactionIndexvariableinsidethisclassasafield.Thus,ifwecreatemorecopiesofthistask,theyallwillshareasingleactionIndexvariable.Atthesametime,themyIndexvariablewillbedifferentforeachcopyofthistask.
Soaschedulingalgorithmcancreateasmanycopiesofthistaskasneeded,andstillitisguaranteedthateveryactionwillbeexecutedatleastonceoronlyonetime.Thisallowsschedulingmechanismstoworkefficiently.First,wecreateasmanycopiesofthethreadsastheCPUsupport.Then,iftasksrunlongerthanacertainamountoftime,theschedulerwillcreatemorecopiestopreventCPUcoresfromidling.Thismakesthetasksrunslightlyslower,butweknowthatourtasksarelong-runningandthiswillnotbeimportantforoverallperformance.
Thisalgorithmalsoensuresthat,evenwhenwehavemanyactionstoberun,therealnumberoftasksthataretobeexecutedinparallelwillbelowandclosetothenumberofthreadsthattheCPUsupports.
www.EBooksWorld.ir
![Page 158: Mastering C# Concurrency - EBooksWorld...a print book customer, you are entitled to a discount on the eBook copy. Get in touch with us at for more details](https://reader035.vdocument.in/reader035/viewer/2022062917/5ed62e6504e9cb4adb670aed/html5/thumbnails/158.jpg)
Parallel.ForandParallel.ForeachThesemethodsareusefultocreateparallelloops.TheyusethesamestrategyasParallel.Invoke,sinceitisveryeffectivewhenhavingalargenumberofiterationstoruninparallel.Parallel.Foreachoffersevenmorecontrol,allowingustouseacustomtaskpartitioningalgorithmwiththePartitioner<T>andOrderablePartitioner<T>abstractclassimplementations.
Toseethedefaultparallelizationstrategy,let’srunthiscode:
privatestaticvoidCalc(intiterations)
{
vartaskIds=newHashSet<int>();
varsum=0;
Parallel.For(
0,
iterations,
i=>
{
Thread.SpinWait(1000000);
lock(taskIds)
taskIds.Add(Task.CurrentId.Value);
});
Console.WriteLine("{0}iterations,{1}tasks",iterations,
taskIds.Count);
}
WesimplycalltheParallel.Formethodwithadifferentnumberofiterationsandcounthowmanyuniquetaskidswe’vegot.
OnamachinewithCorei7-2600KCPU,wewillgetthesevalues:
1iteration,1tasks
4iterations,4tasks
8iterations,8tasks
12iterations,8tasks
16iterations,8tasks
32iterations,9tasks
64iterations,9tasks
TheCPUsupportseightconcurrentthreads,andthealgorithmchoseeighttaskstoruninparalleluntil32iterations,whenoneadditionaltaskisaddedtopreventpossibleCPUidling;thismakesthecodemoreefficient.
www.EBooksWorld.ir
![Page 159: Mastering C# Concurrency - EBooksWorld...a print book customer, you are entitled to a discount on the eBook copy. Get in touch with us at for more details](https://reader035.vdocument.in/reader035/viewer/2022062917/5ed62e6504e9cb4adb670aed/html5/thumbnails/159.jpg)
UnderstandingthetaskschedulerThetaskschedulermanagesandexecutesTPLtasks.First,wewillreviewadefaulttaskscheduleralgorithm,andthenwewilllearnhowtocreateacustomtaskscheduleranduseitwithTPLtoruntasksonit.
Thedefaulttaskschedulerisbasedonthe.NETthreadpoolandusesitsglobalqueuetoruntop-leveltasksthatarenotcreatedinthecontextofanothertask.However,ifwecreateanestedorchildtask,itisputonalocalqueuethatiscreatedonaworkerthreadthatrunstheparenttask.Whenthisworkerthreadgetsreadytorunatask,itfirstlooksforworkitemsonthelocalqueuethatisaccessedinLIFOorder.Usinglocalqueuereducescontentionsincewedonotaccessanyshareddata;thus,thereisnoneedforanysynchronization.
Ifthelocalqueueisempty,theworkerthreadlooksintoaglobalqueue.Ifthisqueueisempty,thentopreventidlingthethreadisgoingtolookatotherthreads’localqueues.Ifthethreadfindsaworkitemhereafterrunningsomeheuristicstodecideiftakingthisworkitemwillbeefficient,thethreadstealsthisworkitemfromanotherthread’slocalqueue.ThestealinghappensinFIFOorderforefficiencyreasons.
ThiswayTPLtriestoimproveperformancebyloweringcontentionandusingCPUcachemoreeffectively,andatthesametime,byloadbalancingbetweenworkerthreadswithawork-stealingalgorithm.
Thedefaultschedulerworkswell,butinsomecases,weneedtoreplaceitwithanother.ImagineaWPFapplicationthathasabuttonclickedeventhandlerwiththefollowingcode:
vart=Task.Factory.StartNew(()=>
{
Console.WriteLine("Id:{0},Isthreadpoolthread:{1}",
Thread.CurrentThread.ManagedThreadId,
Thread.CurrentThread.IsThreadPoolThread);
Thread.Sleep(TimeSpan.FromSeconds(1));
_label.Content=newTextBlock{Text="HellofromTPLtask"};
},
CancellationToken.None,
TaskCreationOptions.None,
TaskScheduler.Default);
while(t.Status!=TaskStatus.RanToCompletion&&t.Status!=
TaskStatus.Faulted)
{
//runmessageloop
Application.Current.Dispatcher.Invoke(
DispatcherPriority.Background,newAction(delegate{}));
}
if(null!=t.Exception)
{
varinnerException=t.Exception.Flatten().InnerException;
Console.WriteLine("{0}:{1}",innerException.GetType(),
www.EBooksWorld.ir
![Page 160: Mastering C# Concurrency - EBooksWorld...a print book customer, you are entitled to a discount on the eBook copy. Get in touch with us at for more details](https://reader035.vdocument.in/reader035/viewer/2022062917/5ed62e6504e9cb4adb670aed/html5/thumbnails/160.jpg)
innerException.Message);
}
Ifwerunthiscode,wewillseethefollowing:
Id:4,Isthreadpoolthread:True
System.InvalidOperationException:ThecallingthreadmustbeSTA,because
manyUIcomponentsrequirethis.
ThereasonisthatwetriedtoaccesstheUIcontrolfromathreadpoolworkerthread,whichisforbidden.Tomakethiscodework,wehavetouseataskschedulerthatwillputthistaskonaUIthread:
vart=Task.Factory.StartNew(()=>
{
Console.WriteLine("Id:{0},Isthreadpoolthread:{1}",
Thread.CurrentThread.ManagedThreadId,
Thread.CurrentThread.IsThreadPoolThread);
Thread.Sleep(TimeSpan.FromSeconds(1));
_label.Content=newTextBlock{Text="HellofromTPLtask"};
},
CancellationToken.None,
TaskCreationOptions.None,
TaskScheduler.FromCurrentSynchronizationContext());
Theoutputwillbedifferent,andtheprogramwillrunsuccessfully,changingthelabelvalue:
Id:1,Isthreadpoolthread:False
TheUIandasynchronyisaverylargeandcomplicatedtopic.Wewillgetbacktothislaterinthisbook.
Lastbutnottheleastisimplementingacustomtaskscheduler.WeneedtoinheritthisfromtheTaskSchedulerclassandimplementseveralabstractmembers:
publicclassSynchronousTaskScheduler:TaskScheduler
{
//wedonotscheduletasks,werunthemsynchronously
protectedoverrideIEnumerable<Task>GetScheduledTasks()
{
returnEnumerable.Empty<Task>();
}
//runthetasksynchronouslyonthecurrentthread
protectedoverridevoidQueueTask(Tasktask)
{
TryExecuteTask(task);
}
//thesamething–justrunthetaskoncurrentthread
protectedoverrideboolTryExecuteTaskInline(
Tasktask,booltaskWasPreviouslyQueued)
{
returnTryExecuteTask(task);
}
www.EBooksWorld.ir
![Page 161: Mastering C# Concurrency - EBooksWorld...a print book customer, you are entitled to a discount on the eBook copy. Get in touch with us at for more details](https://reader035.vdocument.in/reader035/viewer/2022062917/5ed62e6504e9cb4adb670aed/html5/thumbnails/161.jpg)
//maximumconcurrencylevelis1,becauseonlyonetaskrunsat//atime
publicoverrideintMaximumConcurrencyLevel
{
get{return1;}
}
}
Ofcourse,real-worldtaskschedulersaremuchmorecomplicatedthanthisone,butthisworkstoo.Let’susethiswiththepreviouscode:
vart=Task.Factory.StartNew(()=>
{
Console.WriteLine("Id:{0},Isthreadpoolthread:{1}",
Thread.CurrentThread.ManagedThreadId,
Thread.CurrentThread.IsThreadPoolThread);
Thread.Sleep(TimeSpan.FromSeconds(1));
_label.Content=newTextBlock{Text="HellofromTPLtask"};
},
CancellationToken.None,
TaskCreationOptions.None,
newSynchronousTaskScheduler());
Thecodewillworkfineandwewillgetthesameresults.
www.EBooksWorld.ir
![Page 162: Mastering C# Concurrency - EBooksWorld...a print book customer, you are entitled to a discount on the eBook copy. Get in touch with us at for more details](https://reader035.vdocument.in/reader035/viewer/2022062917/5ed62e6504e9cb4adb670aed/html5/thumbnails/162.jpg)
www.EBooksWorld.ir
![Page 163: Mastering C# Concurrency - EBooksWorld...a print book customer, you are entitled to a discount on the eBook copy. Get in touch with us at for more details](https://reader035.vdocument.in/reader035/viewer/2022062917/5ed62e6504e9cb4adb670aed/html5/thumbnails/163.jpg)
SummaryInthischapter,wehavereviewedTaskParallelLibraryindetail.Wehavestudieditsarchitectureandcompositionblocks.Wehavelearnedaboutexceptionhandlingandtaskcancellationindetail.Weexaminedperformanceandlatencyissuesbyfindingoutthebestwayofwritingcodetoachievegoodresults.UsingtheParallelclassAPIallowedustoquicklycreateparallelprograms,anddeep-divingintoTPLtaskschedulingallowedustowriteacustomtaskschedulerandcustomizeTPLtaskexecution.
Inthenextchapter,wewilllearnhowtheC#languagesupportsasynchrony.Wewillunderstanditsnewkeywords,asyncandawait,andunderstandhowwecanuseTaskParallelLibrarywiththenewC#syntax.Also,wewillreviewindetailhowexactlynewlanguagefeaturesworkandcreateourowncustomcodethatwillbecompatiblewiththeawaitstatement.
www.EBooksWorld.ir
![Page 164: Mastering C# Concurrency - EBooksWorld...a print book customer, you are entitled to a discount on the eBook copy. Get in touch with us at for more details](https://reader035.vdocument.in/reader035/viewer/2022062917/5ed62e6504e9cb4adb670aed/html5/thumbnails/164.jpg)
www.EBooksWorld.ir
![Page 165: Mastering C# Concurrency - EBooksWorld...a print book customer, you are entitled to a discount on the eBook copy. Get in touch with us at for more details](https://reader035.vdocument.in/reader035/viewer/2022062917/5ed62e6504e9cb4adb670aed/html5/thumbnails/165.jpg)
Chapter5.C#LanguageSupportforAsynchronyTheTaskParallelLibrarymakesitpossibletocombineasynchronoustasksandsetdependenciesbetweenthem.Inthepreviouschapter,wereviewedthistopicindetail.Howevertogetaclearunderstandinginthischapter,wewillusethisapproachtosolvearealproblem—downloadingimagesfromBing(thesearchengine).Also,wewilldothefollowing:
ImplementstandardsynchronousapproachUseTaskParallelLibrarytocreateanasynchronousversionoftheprogramUseC#5.0built-inasynchronysupporttomakethecodeeasiertoreadandmaintainSimulateC#asynchronousinfrastructurewiththehelpofiteratorsLearnaboutotherusefulfeaturesofTaskParallelLibraryMakeanyC#typecompatiblewithbuilt-inasynchronouskeywords
www.EBooksWorld.ir
![Page 166: Mastering C# Concurrency - EBooksWorld...a print book customer, you are entitled to a discount on the eBook copy. Get in touch with us at for more details](https://reader035.vdocument.in/reader035/viewer/2022062917/5ed62e6504e9cb4adb670aed/html5/thumbnails/166.jpg)
ImplementingthedownloadingofimagesfromBingEverydayBing.compublishesitsbackgroundimagethatcanbeusedasdesktopwallpaper.ThereisanXMLAPItogetinformationaboutthesepicturesthatcanbefoundathttp://www.bing.com/hpimagearchive.aspx.
www.EBooksWorld.ir
![Page 167: Mastering C# Concurrency - EBooksWorld...a print book customer, you are entitled to a discount on the eBook copy. Get in touch with us at for more details](https://reader035.vdocument.in/reader035/viewer/2022062917/5ed62e6504e9cb4adb670aed/html5/thumbnails/167.jpg)
CreatingasimplesynchronoussolutionLet’strytowriteaprogramtodownloadthelasteightimagesfromthissite.Wewillstartbydefiningobjectstostoreimageinformation.Thisiswhereathumbnailimageanditsdescriptionwillbestored:
usingSystem.Drawing;
publicclassWallpaperInfo
{
privatereadonlyImage_thumbnail;
privatereadonlystring_description;
publicWallpaperInfo(Imagethumbnail,stringdescription)
{
_thumbnail=thumbnail;
_description=description;
}
publicImageThumbnail
{
get{return_thumbnail;}
}
publicstringDescription
{
get{return_description;}
}
}
Thenextcontainertypeisforallthedownloadedpicturesandthetimerequiredtodownloadandmakethethumbnailimagesfromtheoriginalpictures:
publicclassWallpapersInfo
{
privatereadonlylong_milliseconds;
privatereadonlyWallpaperInfo[]_wallpapers;
publicWallpapersInfo(longmilliseconds,WallpaperInfo[]wallpapers)
{
_milliseconds=milliseconds;
_wallpapers=wallpapers;
}
publiclongMilliseconds
{
get{return_milliseconds;}
}
publicWallpaperInfo[]Wallpapers
{
get{return_wallpapers;}
}
}
NowweneedtocreatealoaderclasstodownloadimagesfromBing.Weneedtodefineawww.EBooksWorld.ir
![Page 168: Mastering C# Concurrency - EBooksWorld...a print book customer, you are entitled to a discount on the eBook copy. Get in touch with us at for more details](https://reader035.vdocument.in/reader035/viewer/2022062917/5ed62e6504e9cb4adb670aed/html5/thumbnails/168.jpg)
Loaderstaticclassandfollowwithanimplementation.Let’screateamethodthatwillmakeathumbnailimagefromthesourceimagestream:
privatestaticImageGetThumbnail(StreamimageStream)
{
using(imageStream)
{
varfullBitmap=Image.FromStream(imageStream);
returnnewBitmap(fullBitmap,192,108);
}
}
TocommunicateviatheHTTPprotocol,itisrecommendedtousetheSystem.Net.HttpClienttypefromtheSystem.Net.dllassembly.Let’screatethefollowingextensionmethodsthatwillallowustousethePOSTHTTPmethodtodownloadanimageandgetanopenedstream:
privatestaticStreamDownloadData(thisHttpClientclient,stringuri)
{
varresponse=client.PostAsync(
uri,newStringContent(string.Empty)).Result;
returnresponse.Content.ReadAsStreamAsync().Result;
}
privatestaticTask<Stream>DownloadDataAsync(thisHttpClientclient,
stringuri){
Task<HttpResponseMessage>responseTask=client.PostAsync(uri,new
StringContent(string.Empty));
returnresponseTask.ContinueWith(task=>
task.Result.Content.ReadAsStreamAsync()).Unwrap();
}
Tocreatetheeasiestimplementationpossible,wewillimplementdownloadingwithoutanyasynchrony.Here,wewilldefineHTTPendpointsfortheBingAPI:
privateconststring_catalogUri=
"http://www.bing.com/hpimagearchive.aspx?format=xml&idx=0&n=8&mbl=1&mkt=en-
ww";
privateconststring_imageUri="http://bing.com{0}_1920x1080.jpg";
Then,wewillstartmeasuringthetimerequiredtofinishdownloadinganddownloadanXMLcatalogthathasinformationabouttheimagesthatweneed:
varsw=Stopwatch.StartNew();
varclient=newHttpClient();
varcatalogXmlString=client.DownloadString(_catalogUri);
Next,theXMLstringwillbeparsedtoanXMLdocument:
varxDoc=XDocument.Parse(catalogXmlString);
NowusingLINQtoXML,wewillquerytheinformationneededfromthedocumentandrunthedownloadprocessforeachimage:
www.EBooksWorld.ir
![Page 169: Mastering C# Concurrency - EBooksWorld...a print book customer, you are entitled to a discount on the eBook copy. Get in touch with us at for more details](https://reader035.vdocument.in/reader035/viewer/2022062917/5ed62e6504e9cb4adb670aed/html5/thumbnails/169.jpg)
varwallpapers=xDoc
.Root
.Elements("image")
.Select(e=>
new
{
Desc=e.Element("copyright").Value,
Url=e.Element("urlBase").Value
})
.Select(item=>
new
{
item.Desc,
FullImageData=client.DownloadData(
string.Format(_imageUri,item.Url))
})
.Select(item=>
newWallpaperInfo(
GetThumbnail(item.FullImageData),
item.Desc))
.ToArray();
sw.Stop();
ThefirstSelectmethodcallextractstheimageURLanddescriptionfromeachimageXMLelementthatisadirectchildofrootelement.ThisinformationiscontainedinsidetheurlBaseandcopyrightXMLelementsinsidetheimageelement.ThesecondonedownloadsanimagefromtheBingsite.ThelastSelectmethodcreatesathumbnailimageandstoresalltheinformationneededinsidetheWallPaperInfoclassinstance.
Todisplaytheresults,weneedtocreateauserinterface.WindowsFormsisasimpleandfastwaytoimplementthetechnology,sowecanuseittoshowtheresultstotheuser.Thereisabuttonthatrunsthedownload,apaneltoshowthedownloadedpictures,andalabelthatwillshowthetimerequiredtofinishdownloading.
Hereistheimplementationcode.Thisincludesacalculationofthetopco-ordinateforeachelement,acodetodisplaytheimagesandstartthedownloadprocess:
privateintGetItemTop(intheight,intindex)
{
returnindex*(height+8)+8;
}
privatevoidRefreshContent(WallpapersInfoinfo)
{
_resultPanel.Controls.Clear();
_resultPanel.Controls.AddRange(
info.Wallpapers.SelectMany((wallpaper,i)=>newControl[]
{
newPictureBox
{
Left=4,
Image=wallpaper.Thumbnail,
AutoSize=true,
Top=GetItemTop(wallpaper.Thumbnail.Height,i)
www.EBooksWorld.ir
![Page 170: Mastering C# Concurrency - EBooksWorld...a print book customer, you are entitled to a discount on the eBook copy. Get in touch with us at for more details](https://reader035.vdocument.in/reader035/viewer/2022062917/5ed62e6504e9cb4adb670aed/html5/thumbnails/170.jpg)
},
newLabel
{
Left=wallpaper.Thumbnail.Width+8,
Top=GetItemTop(wallpaper.Thumbnail.Height,i),
Text=wallpaper.Description,
AutoSize=true
}
}).ToArray());
_timeLabel.Text=string.Format(
"Time:{0}ms",info.Milliseconds);
}
privatevoid_loadSyncBtn_Click(objectsender,System.EventArgse)
{
varinfo=Loader.SyncLoad();
RefreshContent(info);
}
Theresultlooksasfollows:
www.EBooksWorld.ir
![Page 171: Mastering C# Concurrency - EBooksWorld...a print book customer, you are entitled to a discount on the eBook copy. Get in touch with us at for more details](https://reader035.vdocument.in/reader035/viewer/2022062917/5ed62e6504e9cb4adb670aed/html5/thumbnails/171.jpg)
SothetimetodownloadalltheseimagesshouldbeaboutseveralsecondsiftheInternetconnectionisbroadband.Canwedothisfaster?Wecertainlycan!Nowwewilldownloadandprocesstheimagesonebyone,butwetotallycanprocesseachimageinparallel.
www.EBooksWorld.ir
![Page 172: Mastering C# Concurrency - EBooksWorld...a print book customer, you are entitled to a discount on the eBook copy. Get in touch with us at for more details](https://reader035.vdocument.in/reader035/viewer/2022062917/5ed62e6504e9cb4adb670aed/html5/thumbnails/172.jpg)
CreatingaparallelsolutionwithTaskParallelLibraryInthepreviouschapter,wereviewedTaskParallelLibraryandtherelationshipsbetweentasks.Thecodenaturallysplitsintoseveralstages:
LoadimagescatalogXMLfromBingParsetheXMLdocumentandgettheinformationneededabouttheimagesLoadeachimage’sdatafromBingCreateathumbnailimageforeachimagedownloaded
Theprocesscanbevisualizedwiththedependencychart:
HttpClienthasnaturallyasynchronousAPI,soweonlyneedtocombineeverythingtogetherwiththehelpofaTask.ContinueWithmethod:
publicstaticTask<WallpapersInfo>TaskLoad()
{
varsw=Stopwatch.StartNew();
vardownloadBingXmlTask=newHttpClient().GetStringAsync(
_catalogUri);
varparseXmlTask=downloadBingXmlTask.ContinueWith(task=>
{
varxmlDocument=XDocument.Parse(task.Result);
returnxmlDocument.Root
.Elements("image")
.Select(e=>
new
{
Description=e.Element("copyright").Value,
Url=e.Element("urlBase").Value
www.EBooksWorld.ir
![Page 173: Mastering C# Concurrency - EBooksWorld...a print book customer, you are entitled to a discount on the eBook copy. Get in touch with us at for more details](https://reader035.vdocument.in/reader035/viewer/2022062917/5ed62e6504e9cb4adb670aed/html5/thumbnails/173.jpg)
});
});
vardownloadImagesTask=parseXmlTask.ContinueWith(
task=>Task.WhenAll(
task.Result.Select(item=>newHttpClient()
.DownloadDataAsync(string.Format(_imageUri,item.Url))
.ContinueWith(downloadTask=>newWallpaperInfo(
GetThumbnail(downloadTask.Result),item.Description)))))
.Unwrap();
returndownloadImagesTask.ContinueWith(task=>
{
sw.Stop();
returnnewWallpapersInfo(sw.ElapsedMilliseconds,task.Result);
});
}
Thecodehassomeinterestingmoments.ThefirsttaskiscreatedbytheHttpClientinstance,anditcompleteswhenthedownloadprocesssucceeds.Nowwewillattachasubsequenttask,whichwillusetheXMLstringdownloadedbytheprevioustask,andthenwewillcreateanXMLdocumentfromthisstringandextracttheinformationneeded.
Nowthisisbecomingmorecomplicated.Wewanttocreateatasktodownloadeachimageandcontinueuntilallthesetaskscompletesuccessfully.SowewillusetheLINQSelectmethodtorundownloadsforeachimagethatwasdefinedintheXMLcatalog,andafterthedownloadprocesscompletes,wewillcreateathumbnailimageandstoretheinformationintheWallpaperInfoinstance.ThiscreatesIEnumerable<Task<WallpaperInfo>>asaresult,andtowaitforallthesetaskstocomplete,wewillusetheTask.WhenAllmethod.However,thisisataskthatisinsideacontinuationtask,andtheresultisgoingtobeoftheTask<Task<WallpaperInfo[]>>type.Togettheinnertask,wewillusetheUnwrapmethod,whichhasthefollowingsyntax:
publicstaticTaskUnwrap(thisTask<Task>task)
ThiscanbeusedonanyTask<Task>instanceandwillcreateaproxytaskthatrepresentsanentireasynchronousoperationproperly.
Thelasttaskistostopthetimerandreturnthedownloadedimagesandisquitestraightforward.WehavetoaddanotherbuttontotheUItorunthisimplementation.Noticetheimplementationofthebuttonclickhandler:
privatevoid_loadTaskBtn_Click(objectsender,System.EventArgse)
{
varinfo=Loader.TaskLoad();
info.ContinueWith(task=>RefreshContent(task.Result),
CancellationToken.None,
TaskContinuationOptions.None,
TaskScheduler.FromCurrentSynchronizationContext());
}
SincetheTaskLoadmethodisasynchronous,itreturnsimmediately.Todisplaytheresults,
www.EBooksWorld.ir
![Page 174: Mastering C# Concurrency - EBooksWorld...a print book customer, you are entitled to a discount on the eBook copy. Get in touch with us at for more details](https://reader035.vdocument.in/reader035/viewer/2022062917/5ed62e6504e9cb4adb670aed/html5/thumbnails/174.jpg)
wehavetodefineacontinuationtask.However,fromthepreviouschapteryoualreadyknowthatthedefaulttaskschedulerwillrunataskcodeonathreadpoolworkerthread.ToworkwithUIcontrols,wehavetorunthecodeontheUIthread,andweuseataskschedulerthatcapturesthecurrentsynchronizationcontextandrunsthecontinuationtaskonthis.WewillcoversynchronizationcontextandtherelatedinfrastructurelaterinChapter8,Server-SideAsynchrony,andChapter9,ConcurrencyintheUserInterface,whereserver-sideandclient-sideasynchronywillbereviewedindetail.
Let’snamethebuttonasLoadusingTPLandtesttheresults.IfyourInternetconnectionisfast,thisimplementationwilldownloadtheimagesinparallelmuchfastercomparedtotheprevioussequentialdownloadprocess.
Ifwelookbackatthecode,wewillseethatitisquitehardtounderstandwhatitactuallydoes.Wecanseehowonetaskdependsontheother,buttheoriginalgoalisuncleardespitethecodebeingverycompactandeasy.Imaginewhatwillhappenifwetrytoaddexceptionhandlinghere.Wewouldhavetoappendanadditionalcontinuationtaskwithexceptionhandlingtoeachtask.Thiswillbemuchhardertoreadandunderstand.Inareal-worldprogram,itwillbeachallengingtasktokeepinmindthesetaskscompositionandsupportacodewritteninsuchaparadigm.
www.EBooksWorld.ir
![Page 175: Mastering C# Concurrency - EBooksWorld...a print book customer, you are entitled to a discount on the eBook copy. Get in touch with us at for more details](https://reader035.vdocument.in/reader035/viewer/2022062917/5ed62e6504e9cb4adb670aed/html5/thumbnails/175.jpg)
EnhancingthecodewithC#5.0built-insupportforasynchronyFortunately,C#5.0introducedtheasyncandawaitkeywordsthatareintendedtomakeasynchronouscodelooksynchronous,andthus,makesreadingofcodeandunderstandingtheprogramfloweasier.However,thisisanotherabstractionandithidesmanythingsthathappenunderthehoodfromtheprogrammer,whichinseveralsituationsisnotagoodthing.Thepotentialpitfallsandsolutionswillbecoveredlaterinthisbook,butnowlet’srewritethepreviouscodeusingnewC#5.0features:
publicstaticasyncTask<WallpapersInfo>AsyncLoad()
{
varsw=Stopwatch.StartNew();
varclient=newHttpClient();
varcatalogXmlString=awaitclient.GetStringAsync(_catalogUri);
varxDoc=XDocument.Parse(catalogXmlString);
varwallpapersTask=xDoc
.Root
.Elements("image")
.Select(e=>
new
{
Description=e.Element("copyright").Value,
Url=e.Element("urlBase").Value
})
.Select(asyncitem=>
new
{
item.Description,
FullImageData=awaitclient.DownloadDataAsync(
string.Format(_imageUri,item.Url))
});
varwallpapersItems=awaitTask.WhenAll(wallpapersTask);
varwallpapers=wallpapersItems.Select(
item=>newWallpaperInfo(
GetThumbnail(item.FullImageData),item.Description));
sw.Stop();
returnnewWallpapersInfo(sw.ElapsedMilliseconds,
wallpapers.ToArray());
}
Nowthecodelooksalmostlikethefirstsynchronousimplementation.TheAsyncLoadmethodhasaasyncmodifierandaTask<T>returnvalue,andsuchmethodsmustalwaysreturnTaskorbedeclaredasvoid—thisisenforcedbythecompiler.However,inthemethod’scode,thetypethatisreturnedisjustT.Thisisstrangeatfirst,butthemethod’sreturnvaluewillbeeventuallyturnedintoTask<T>bytheC#5.0compiler.Theasyncmodifierisnecessarytouseawaitinsidethemethod.Inthefurthercode,thereisawait
www.EBooksWorld.ir
![Page 176: Mastering C# Concurrency - EBooksWorld...a print book customer, you are entitled to a discount on the eBook copy. Get in touch with us at for more details](https://reader035.vdocument.in/reader035/viewer/2022062917/5ed62e6504e9cb4adb670aed/html5/thumbnails/176.jpg)
insidealambdaexpression,andweneedtomarkthislambdaasasyncaswell.
Sowhatisgoingonwhenweuseawaitinsideourcode?Itdoesnotalwaysmeanthatthecallisactuallyasynchronous.Itcanhappenthatbythetimewecallthemethod,theresultisalreadyavailable,sowejustgettheresultandproceedfurther.However,themostcommoncaseiswhenwemakeanasynchronouscall.Inthiscase,westart,forexample,bydownloadingaXMLstringfromBingviaHTTPandimmediatelyreturnataskthatisacontinuationtaskandcontainstherestofthecodeafterthelinewithawait.
Torunthis,weneedtoaddanotherbuttonnamedLoadusingasync.Wearegoingtouseawaitinthebuttonclickeventhandleraswell,soweneedtomarkitwiththeasyncmodifier:
privateasyncvoid_loadAsyncBtn_Click(objectsender,System.EventArgse)
{
varinfo=awaitLoader.AsyncLoad();
RefreshContent(info);
}
Nowifthecodeafterawaitisbeingruninacontinuationtask,whyistherenomultithreadedaccessexception?TheRefreshContentmethodrunsinanothertask,buttheC#compilerisawareofthesynchronizationcontextandgeneratesacodethatexecutesthecontinuationtaskontheUIthread.TheresultshouldbeasfastasaTPLimplementationbutthecodeismuchcleanerandeasytofollow.
Lastbutnotleast,isthepossibilitytoputasynchronousmethodcallsinsideatryblock.TheC#compilergeneratesacodethatwillpropagatetheexceptionintothecurrentcontextandunwraptheAggregateExceptioninstancetogettheoriginalexceptionfromit.
NoteInC#5.0,itwasimpossibletouseawaitinsidecatchandfinallyblocks,butC#6.0introducedanewasync/awaitinfrastructureandthislimitationwasremoved.
www.EBooksWorld.ir
![Page 177: Mastering C# Concurrency - EBooksWorld...a print book customer, you are entitled to a discount on the eBook copy. Get in touch with us at for more details](https://reader035.vdocument.in/reader035/viewer/2022062917/5ed62e6504e9cb4adb670aed/html5/thumbnails/177.jpg)
SimulatingC#asynchronousinfrastructurewithiteratorsTodigintotheimplementationdetails,itmakessensetolookatthedecompiledcodeoftheAsyncLoadmethod:
publicstaticTask<WallpapersInfo>AsyncLoad()
{
Loader.<AsyncLoad>d__21stateMachine;
stateMachine.<>t__builder=
AsyncTaskMethodBuilder<WallpapersInfo>.Create();
stateMachine.<>1__state=-1;
stateMachine
.<>t__builder
.Start<Loader.<AsyncLoad>d__21>(refstateMachine);
returnstateMachine.<>t__builder.Task;
}
Themethodbodywasreplacedbyacompiler-generatedcodethatcreatesaspecialkindofstatemachine.Wewillnotreviewthefurtherimplementationdetailshere,becauseitisquitecomplicatedandissubjecttochangesfromversiontoversion.However,what’sgoingonisthatthecodegetsdividedintoseparatepiecesateachlinewhereawaitispresent,andeachpiecebecomesaseparatestateinthegeneratedstatemachine.Then,aspecialSystem.Runtime.CompilerServices.AsyncTaskMethodBuilderstructurecreatesTaskthatrepresentsthegeneratedstatemachineworkflow.
Thisstatemachineisquitesimilartotheonethatisgeneratedfortheiteratormethodsthatleveragetheyieldkeyword.InC#6.0,thesameuniversalcodegetsgeneratedforthecodecontainingyieldandawait.Toillustratethegeneralprinciplesbehindthegeneratedcode,wecanuseiteratormethodstoimplementanotherversionofasynchronousimagesdownloadfromBing.
Therefore,wecanturnanasynchronousmethodintoaniteratormethodthatreturnstheIEnumerable<Task>instance.WereplaceeachawaitwithyieldreturnmakingeachiterationtobereturnedasTask.Torunsuchamethod,weneedtoexecuteeachtaskandreturnthefinalresult.ThiscodecanbeconsideredasananalogueofAsyncTaskMethodBuilder:
privatestaticTask<TResult>ExecuteIterator<TResult>(
Func<Action<TResult>,IEnumerable<Task>>iteratorGetter)
{
returnTask.Run(()=>
{
varresult=default(TResult);
foreach(vartaskiniteratorGetter(res=>result=res))
task.Wait();
returnresult;
});
www.EBooksWorld.ir
![Page 178: Mastering C# Concurrency - EBooksWorld...a print book customer, you are entitled to a discount on the eBook copy. Get in touch with us at for more details](https://reader035.vdocument.in/reader035/viewer/2022062917/5ed62e6504e9cb4adb670aed/html5/thumbnails/178.jpg)
}
Weiteratethrougheachtaskandawaititscompletion.Sincewecannotusetheoutandrefparametersiniteratormethods,weusealambdaexpressiontoreturntheresultfromeachtask.Tomakethecodeeasiertounderstand,wehavecreatedanewcontainertaskandusedtheforeachloop;however,tobeclosertotheoriginalimplementation,weshouldgetthefirsttaskandusetheContinueWithmethodprovidingthenexttasktoitandcontinueuntilthelasttask.Inthiscase,wewillenduphavingonefinaltaskrepresentinganentiresequenceofasynchronousoperations,butthecodewillbecomemorecomplicatedaswell.
SinceitisnotpossibletousetheyieldkeywordinsidealambdaexpressionsinthecurrentC#versions,wewillimplementimagedownloadandthumbnailgenerationasaseparatemethod:
privatestaticIEnumerable<Task>GetImageIterator(
stringurl,
stringdesc,
Action<WallpaperInfo>resultSetter)
{
varloadTask=newHttpClient().DownloadDataAsync(
string.Format(_imageUri,url));
yieldreturnloadTask;
varthumbTask=Task.FromResult(GetThumbnail(loadTask.Result));
yieldreturnthumbTask;
resultSetter(newWallpaperInfo(thumbTask.Result,desc));
}
ItlookslikeacommonC#asynccodewithyieldreturnusedinsteadoftheawaitkeywordandresultSetterusedinsteadofreturn.NoticetheTask.FromResultmethodthatweusedtogetTaskfromthesynchronousGetThumbnailmethod.WecanuseTask.Runandputthisoperationonaseparateworkerthread,butitwillbeanineffectivesolution.Task.FromResultallowsustogetTaskthatisalreadycompletedandhasaresult.Ifyouuseawaitwithsuchtask,itwillbetranslatedintoasynchronouscall.
Themaincodecanberewritteninthesameway:
privatestaticIEnumerable<Task>GetWallpapersIterator(
Action<WallpaperInfo[]>resultSetter)
{
varcatalogTask=newHttpClient().GetStringAsync(_catalogUri);
yieldreturncatalogTask;
varxDoc=XDocument.Parse(catalogTask.Result);
varimagesTask=Task.WhenAll(xDoc
.Root
.Elements("image")
.Select(e=>new
{
Description=e.Element("copyright").Value,
www.EBooksWorld.ir
![Page 179: Mastering C# Concurrency - EBooksWorld...a print book customer, you are entitled to a discount on the eBook copy. Get in touch with us at for more details](https://reader035.vdocument.in/reader035/viewer/2022062917/5ed62e6504e9cb4adb670aed/html5/thumbnails/179.jpg)
Url=e.Element("urlBase").Value
})
.Select(item=>ExecuteIterator<WallpaperInfo>(
resSetter=>GetImageIterator(
item.Url,item.Description,resSetter))));
yieldreturnimagesTask;
resultSetter(imagesTask.Result);
}
Thiscombineseverythingtogether:
publicstaticWallpapersInfoIteratorLoad()
{
varsw=Stopwatch.StartNew();
varwallpapers=ExecuteIterator<WallpaperInfo[]>(GetWallpapersIterator)
.Result;
sw.Stop();
returnnewWallpapersInfo(sw.ElapsedMilliseconds,wallpapers);
}
Torunthis,wewillcreateonemorebuttoncalledLoadusingiterator.ThebuttonclickhandlerjustrunstheIteratorLoadmethodandthenrefreshestheUI.Thisalsoworkswithaboutthesamespeedasotherasynchronousimplementations.
ThisexamplecanhelpustounderstandthelogicbehindtheC#codegenerationforasynchronousmethodsusedwithawait.Ofcourse,therealcodeismuchmorecomplicated,buttheprinciplesbehinditremainthesame.
www.EBooksWorld.ir
![Page 180: Mastering C# Concurrency - EBooksWorld...a print book customer, you are entitled to a discount on the eBook copy. Get in touch with us at for more details](https://reader035.vdocument.in/reader035/viewer/2022062917/5ed62e6504e9cb4adb670aed/html5/thumbnails/180.jpg)
www.EBooksWorld.ir
![Page 181: Mastering C# Concurrency - EBooksWorld...a print book customer, you are entitled to a discount on the eBook copy. Get in touch with us at for more details](https://reader035.vdocument.in/reader035/viewer/2022062917/5ed62e6504e9cb4adb670aed/html5/thumbnails/181.jpg)
Istheasynckeywordreallyneeded?Itisacommonquestionaboutwhydoweneedtomarkmethodsasasync.WehavealreadymentionediteratormethodsinC#andtheyieldkeyword.Thisisverysimilartoasync/await,andyetwedonotneedtomarkiteratormethodswithanymodifier.TheC#compilerisabletodeterminethatitisaniteratormethodwhenitmeetstheyieldreturnoryieldbreakoperatorsinsidesuchamethod.Sothequestionis,whyisitnotthesamewithawaitandtheasynchronousmethods?
ThereasonisthatasynchronysupportwasintroducedinthelatestC#version,anditisveryimportantnottobreakanylegacycodewhilechangingthelanguage.Imagineifanycodeusedawaitasanameforafieldorvariable.IfC#developersmakeawaitakeywordwithoutanyconditions,thisoldcodewillbreakandstopcompiling.Thecurrentapproachguaranteesthatifwedonotmarkamethodwithasync,theoldcodewillcontinuetowork.
www.EBooksWorld.ir
![Page 182: Mastering C# Concurrency - EBooksWorld...a print book customer, you are entitled to a discount on the eBook copy. Get in touch with us at for more details](https://reader035.vdocument.in/reader035/viewer/2022062917/5ed62e6504e9cb4adb670aed/html5/thumbnails/182.jpg)
www.EBooksWorld.ir
![Page 183: Mastering C# Concurrency - EBooksWorld...a print book customer, you are entitled to a discount on the eBook copy. Get in touch with us at for more details](https://reader035.vdocument.in/reader035/viewer/2022062917/5ed62e6504e9cb4adb670aed/html5/thumbnails/183.jpg)
Fire-and-forgettasksBesidesTaskandTask<T>,wecandeclareanasynchronousmethodasvoid.Itisusefulinthecaseoftop-leveleventhandlers,forexample,thebuttonclickortextchangedhandlersintheUI.Aneventhandlerthatreturnsavalueispossible,butisveryinconvenienttouseanddoesnotmakemuchsense.
Soallowingasyncvoidmethodsmakesitpossibletouseawaitinsidesucheventhandlers:
privateasyncvoidbutton1_Click(objectsender,EventArgse)
{
awaitSomeAsyncStuff();
}
Itseemsthatnothingbadishappening,andtheC#compilergeneratesalmostthesamecodeasfortheTaskreturningmethod,butthereisanimportantcatchrelatedtoexceptionshandling.
WhenanasynchronousmethodreturnsTask,exceptionsareconnectedtothistaskandcanbehandledbothbyTPLandthetry/catchblockincaseawaitisused.However,ifwehaveaasyncvoidmethod,wehavenoTasktoattachtheexceptionstoandthoseexceptionsjustgetpostedtothecurrentsynchronizationcontext.TheseexceptionscanbeobservedusingAppDomain.UnhandledExceptionorsimilareventsinaGUIapplication,butthisisveryeasytomissandnotagoodpractice.
Theotherproblemisthatwecannotuseavoidreturningasynchronousmethodwithawait,sincethereisnoreturnvaluethatcanbeusedtoawaitonit.Wecannotcomposesuchamethodwithotherasynchronoustasksandparticipateintheprogramworkflow.Itisbasicallyafire-and-forgetoperationthatwestart,andthenwehavenowaytocontrolhowitwillproceed(ifwedidnotwritethecodeforthisexplicitly).
Anotherproblemisvoidreturningasynclambdaexpression.Itisveryhardtonoticethatlambdareturnsvoid,andallproblemsrelatedtousualmethodsarerelatedtolambdaexpressionaswell.Imaginethatwewanttorunsomeoperationinparallel.Fromthepreviouschapterwelearnedthattoachievethis,wecanusetheParallel.ForEachmethod.Todownloadsomenewsinparallel,wecanwriteacodelikethis:
Parallel.ForEach(Enumerable.Range(1,10),asynci=>
{
varnews=awaitnewsClient.GetTopNews(i);
newsCollection.Add(news);
});
However,thiswillnotwork,becausethesecondparameteroftheForEachmethodisAction<T>,whichisavoidreturningdelegate.Thus,wewillspawn10downloadprocesses,butsincewecannotwaitforcompletion,weabandonallasynchronousoperationsthatwejuststartedandignoretheresults.
Ageneralruleofthumbistoavoidusingasyncvoidmethods.Ifthisisinevitableandthereisaneventhandler,thenalwayswraptheinnerawaitmethodcallsintry/catchblocksandprovideexceptionhandling.
www.EBooksWorld.ir
![Page 184: Mastering C# Concurrency - EBooksWorld...a print book customer, you are entitled to a discount on the eBook copy. Get in touch with us at for more details](https://reader035.vdocument.in/reader035/viewer/2022062917/5ed62e6504e9cb4adb670aed/html5/thumbnails/184.jpg)
www.EBooksWorld.ir
![Page 185: Mastering C# Concurrency - EBooksWorld...a print book customer, you are entitled to a discount on the eBook copy. Get in touch with us at for more details](https://reader035.vdocument.in/reader035/viewer/2022062917/5ed62e6504e9cb4adb670aed/html5/thumbnails/185.jpg)
www.EBooksWorld.ir
![Page 186: Mastering C# Concurrency - EBooksWorld...a print book customer, you are entitled to a discount on the eBook copy. Get in touch with us at for more details](https://reader035.vdocument.in/reader035/viewer/2022062917/5ed62e6504e9cb4adb670aed/html5/thumbnails/186.jpg)
OtherusefulTPLfeaturesTaskParallelLibraryhasalargecodebaseandsomeusefulfeaturessuchasTask.UnwraporTask.FromResultthatarenotverywellknowntodevelopers.Wehavestillnotmentionedtwomoreextremelyusefulmethodsyet.Theyarecoveredinthefollowingsections.
www.EBooksWorld.ir
![Page 187: Mastering C# Concurrency - EBooksWorld...a print book customer, you are entitled to a discount on the eBook copy. Get in touch with us at for more details](https://reader035.vdocument.in/reader035/viewer/2022062917/5ed62e6504e9cb4adb670aed/html5/thumbnails/187.jpg)
Task.DelayOften,itisrequiredtowaitforacertainamountoftimeinthecode.OneofthetraditionalwaystowaitisusingtheThread.Sleepmethod.TheproblemisthatThread.Sleepblocksthecurrentthread,anditisnotasynchronous.
Anotherdisadvantageisthatwecannotcancelwaitingifsomethinghashappened.Toimplementasolutionforthis,wewillhavetousesystemsynchronizationprimitivessuchasanevent,butthisisnotveryeasytocode.Tokeepthecodesimple,wecanusetheTask.Delaymethod:
//Dosomething
awaitTask.Delay(1000);
//Dosomething
ThismethodcanbecanceledwithahelpoftheCancellationTokeninfrastructureandusessystemtimerunderthehood,sothiskindofwaitingistrulyasynchronous.
www.EBooksWorld.ir
![Page 188: Mastering C# Concurrency - EBooksWorld...a print book customer, you are entitled to a discount on the eBook copy. Get in touch with us at for more details](https://reader035.vdocument.in/reader035/viewer/2022062917/5ed62e6504e9cb4adb670aed/html5/thumbnails/188.jpg)
Task.YieldSometimesweneedapartofthecodetobeguaranteedtorunasynchronously.Forexample,weneedtokeeptheUIresponsive,ormaybewewouldliketoimplementafine-grainedscenario.Anyway,aswealreadyknowthatusingawaitdoesnotmeanthatthecallwillbeasynchronous.Ifwewanttoreturncontrolimmediatelyandruntherestofthecodeasacontinuationtask,wecanusetheTask.Yieldmethod:
//Dosomething
awaitTask.Yield();
//Dosomething
Task.Yieldjustcausesacontinuationtobepostedonthecurrentsynchronizationcontext,orifthesynchronizationcontextisnotavailable,acontinuationwillbepostedonathreadpoolworkerthread.
www.EBooksWorld.ir
![Page 189: Mastering C# Concurrency - EBooksWorld...a print book customer, you are entitled to a discount on the eBook copy. Get in touch with us at for more details](https://reader035.vdocument.in/reader035/viewer/2022062917/5ed62e6504e9cb4adb670aed/html5/thumbnails/189.jpg)
www.EBooksWorld.ir
![Page 190: Mastering C# Concurrency - EBooksWorld...a print book customer, you are entitled to a discount on the eBook copy. Get in touch with us at for more details](https://reader035.vdocument.in/reader035/viewer/2022062917/5ed62e6504e9cb4adb670aed/html5/thumbnails/190.jpg)
ImplementingacustomawaitabletypeUntilnowwehaveonlyusedTaskwiththeawaitoperator.However,itisnottheonlytypethatiscompatiblewithawait.Actually,theawaitoperatorcanbeusedwitheverytypethatcontainstheGetAwaitermethodwithnoparametersandthereturntypethatdoesthefollowing:
ImplementstheINotifyCompletioninterfaceContainstheIsCompletedbooleanpropertyHastheGetResultmethodwithnoparameters
Thismethodcanevenbeanextensionmethod,soitispossibletoextendtheexistingtypesandaddtheawaitcompatibilitytothem.Inthisexample,wewillcreatesuchamethodfortheUritype.ThismethodwilldownloadcontentasastringviaHTTPfromtheaddressprovidedintheUriinstance:
privatestaticTaskAwaiter<string>GetAwaiter(thisUriurl)
{
returnnewHttpClient().GetStringAsync(url).GetAwaiter();
}
varcontent=awaitnewUri("http://google.com");
Console.WriteLine(content.Substring(0,10));
Ifwerunthis,wewillseethefirst10charactersoftheGooglewebsitecontent.
Asyoumaynotice,hereweusedtheTasktypeindirectly,returningthealreadyprovidedawaitermethodfortheTasktype.Wecanimplementanawaitermethodmanuallyfromscratch,butitreallydoesnotmakeanysense.TounderstandhowthisworksitwillbeenoughtocreateacustomwrapperaroundanalreadyexistingTaskAwaiter:
structDownloadAwaiter:INotifyCompletion
{
privatereadonlyTaskAwaiter<string>_awaiter;
publicDownloadAwaiter(Uriuri)
{
Console.WriteLine("Startdownloadingfrom{0}",uri);
vartask=newHttpClient().GetStringAsync(uri);
_awaiter=task.GetAwaiter();
Task.GetAwaiter().OnCompleted(()=>Console.WriteLine("download
completed"));
}
publicboolIsCompleted
{
get{return_awaiter.IsCompleted;}
}
publicvoidOnCompleted(Actioncontinuation)
{
_awaiter.OnCompleted(continuation);
}
www.EBooksWorld.ir
![Page 191: Mastering C# Concurrency - EBooksWorld...a print book customer, you are entitled to a discount on the eBook copy. Get in touch with us at for more details](https://reader035.vdocument.in/reader035/viewer/2022062917/5ed62e6504e9cb4adb670aed/html5/thumbnails/191.jpg)
publicstringGetResult()
{
return_awaiter.GetResult();
}
}
Withthiscode,wehavecustomizedasynchronousexecutionthatprovidesdiagnosticinformationtotheconsole.TogetridofTaskAwaiter,itwillbeenoughtochangetheOnCompletedmethodwithcustomcodethatwillexecutesomeoperationandthenacontinuationprovidedinthismethod.
Tousethiscustomawaiter,weneedtochangeGetAwaiteraccordingly:
privatestaticDownloadAwaiterGetAwaiter(thisUriuri)
{
returnnewDownloadAwaiter(uri);
}
Ifwerunthis,wewillseeadditionalinformationontheconsole.Thiscanbeusefulfordiagnosticsanddebugging.
www.EBooksWorld.ir
![Page 192: Mastering C# Concurrency - EBooksWorld...a print book customer, you are entitled to a discount on the eBook copy. Get in touch with us at for more details](https://reader035.vdocument.in/reader035/viewer/2022062917/5ed62e6504e9cb4adb670aed/html5/thumbnails/192.jpg)
www.EBooksWorld.ir
![Page 193: Mastering C# Concurrency - EBooksWorld...a print book customer, you are entitled to a discount on the eBook copy. Get in touch with us at for more details](https://reader035.vdocument.in/reader035/viewer/2022062917/5ed62e6504e9cb4adb670aed/html5/thumbnails/193.jpg)
SummaryInthischapter,welookedattheC#languageinfrastructurethatsupportsasynchronouscalls.WecoveredthenewC#keywords,asyncandawait,andhowwecanuseTaskParallelLibrarywiththenewC#syntax.WelearnedhowC#generatescodeandcreatesastatemachinethatrepresentsanasynchronousoperation,andweimplementedananaloguesolutionwiththehelpofiteratormethodsandtheyieldkeyword.Besidesthis,westudiedadditionalTaskParallelLibraryfeaturesandlookedathowwecanuseawaitwithanycustomtype.
Inthenextchapter,wewilllearnaboutdatastructuresthatarebuiltforconcurrencyandcommonalgorithmsthatrelyonthem.
www.EBooksWorld.ir
![Page 194: Mastering C# Concurrency - EBooksWorld...a print book customer, you are entitled to a discount on the eBook copy. Get in touch with us at for more details](https://reader035.vdocument.in/reader035/viewer/2022062917/5ed62e6504e9cb4adb670aed/html5/thumbnails/194.jpg)
www.EBooksWorld.ir
![Page 195: Mastering C# Concurrency - EBooksWorld...a print book customer, you are entitled to a discount on the eBook copy. Get in touch with us at for more details](https://reader035.vdocument.in/reader035/viewer/2022062917/5ed62e6504e9cb4adb670aed/html5/thumbnails/195.jpg)
Chapter6.UsingConcurrentDataStructuresChoosinganappropriatedatastructureforyourconcurrentalgorithmisacrucialstep.Wehavealreadylearnedfromthepreviouschaptersthatitisnotusuallypossibletousejustany.NETobjectasashareddatainamultithreadedprogram.Wecanassumethatmostofthecommontypesin.NETareimplementedinsuchawaythattheirstaticmembersarethread-safe,whiletheirinstancemembersarenot.However,onlythoseobjectsthatarespecificallydesignedtobethread-safecanbeusedastheyareinamultithreadedenvironment.
Therefore,ifweneedmultiplethreadstoaddsomeitemtoacollection,wecannotjustcalltheAddmethodofasharedinstanceoftheList<T>type.Itwillleadtounpredictableresults,andmostprobablytheprogramwillendupthrowingaweirdexception.
Thus,inthissituation,therearetwogeneralwaystofollow:eitherweimplementsynchronizedaccesstothestandardcollectionourselveswiththehelpofexistingsynchronizationprimitives,orwecanuseexistingconcurrentcollectionsfromtheSystem.Collections.Concurrentnamespace.
Inthischapter,wearegoingtodigintothedetailsofusingdatastructuresinconcurrentapplicationsandreviewadvantagesanddisadvantagesofeachoption.
www.EBooksWorld.ir
![Page 196: Mastering C# Concurrency - EBooksWorld...a print book customer, you are entitled to a discount on the eBook copy. Get in touch with us at for more details](https://reader035.vdocument.in/reader035/viewer/2022062917/5ed62e6504e9cb4adb670aed/html5/thumbnails/196.jpg)
StandardcollectionsandsynchronizationprimitivesTohighlightwhatproblemscanappearwhenweusenonthreadsafecollectionsinaconcurrentprogram,let’swriteasimpleprogramthatwillusetheParallel.Foreachclasstocopyacollectionanddoubleitselements:
varsource=Enumerable.Range(1,42000).ToList();
vardestination=newList<int>();
Parallel.ForEach(source,n=>destination.Add(n*2));
Assert.AreEqual(source.Count,destination.Count);
Ifwerunthiscode,wewillalmostcertainlygettheAggregateExceptionexceptionwiththeArgumentExceptioninstancewrappedinsideit.
ThishappensbecausetheAddmethodoftheList<T>classisnotthreadsafe,andthereasonforthisliesintheimplementationdetails:
publicvoidAdd(Titem)
{
if(_size==_items.Length)EnsureCapacity(_size+1);
_items[_size++]=item;
_version++;
}
Incasetheconcurrentthreadsaccessthismethodwhenthe_size==items.Length–1conditionistrue,theArgumentExceptionexceptionwillalmostcertainlyoccur.Theimplementationwillcausethecollectiontohaveaninconsistentstate;araceconditionwillleadtheinnerarraynewsizetobelessthanneeded.
Toavoidaracecondition,wecanimplementsomesortofsynchronizationforsharedcollectionaccessusingthelockstatement:
objectsyncRoot=newobject();
varsource=Enumerable.Range(1,42000).ToList();
vardestination=newList<int>();
Parallel.ForEach(source,
n=>
{
lock(syncRoot)
{
destination.Add(n*2);
}
});
Assert.AreEqual(source.Count,destination.Count);
Thiscodewillrunwithouterrors.However,itsefficiencywillbelessthandoingthesamejobfromasinglethread.Insteadofdoingcalculations,athreadwillbewaitingforasharedresource(inthiscase,itisthedestinationvariable)access.Thissituationis
www.EBooksWorld.ir
![Page 197: Mastering C# Concurrency - EBooksWorld...a print book customer, you are entitled to a discount on the eBook copy. Get in touch with us at for more details](https://reader035.vdocument.in/reader035/viewer/2022062917/5ed62e6504e9cb4adb670aed/html5/thumbnails/197.jpg)
calledthreadcontention,anditcansignificantlydecreaseyourprogramperformance.
TousealltheavailableCPUcoreseffectively,wealwayshavetotrytoreducecontentionasmuchaspossible.Insomecases,itispossibletousespecialsynchronizationprimitivesorlock-freealgorithms,orusethreadlocalcomputations,whicharemergedattheendofparallelcalculationstogetthefinalresult.
www.EBooksWorld.ir
![Page 198: Mastering C# Concurrency - EBooksWorld...a print book customer, you are entitled to a discount on the eBook copy. Get in touch with us at for more details](https://reader035.vdocument.in/reader035/viewer/2022062917/5ed62e6504e9cb4adb670aed/html5/thumbnails/198.jpg)
www.EBooksWorld.ir
![Page 199: Mastering C# Concurrency - EBooksWorld...a print book customer, you are entitled to a discount on the eBook copy. Get in touch with us at for more details](https://reader035.vdocument.in/reader035/viewer/2022062917/5ed62e6504e9cb4adb670aed/html5/thumbnails/199.jpg)
ImplementingacachewithReaderWriterLockSlimCachingisacommontechniquethatisbeingusedinmanyapplicationstoincreaseperformanceandefficiency.Usually,readingfromacacheoccursmoreoftenthanwritingoperation,andthenumberofcachereadersishigherthatthenumberofwriters.
Inthisparticularcase,thereisnosenseinusinganexclusivelockpreventingotherthreadsfromreadinganothercachevalue.Thereisabuilt-insynchronizationobjectthathasexactlythisbehavior,anditiscalledReaderWriterLockSlim.
NoteThereareseveralclassesinthe.NETFrameworkinsidetheSystem.Threadingnamespace,whosenamesendwithSlim.ItisusuallymoreefficientandlightweighttoimplementthecorrespondingclasseswithoutSlimattheendoftheirnames.Inmostcases,youshouldprefertheSlimversionsoveroriginalones,unlessyouare100%surewhyyouneednon-slimobjects.ThisruleworkswiththeReaderWriterLockandReaderWriterLockSlimclassesaswell—alwayspreferaSlimobject,becauseithasmajorefficiencyandcorrectiveimprovements.
Cachecanbeuseddifferentlyintheapplication,butthemostcommonapproachisusingcacheasidepattern.Theclientisunawareofcaching;ifthereisalong-runningoperationandnoresultofthisoperationcanbefoundinthecache,weperformtheoperationandsavetheresultintothecache.Ifthereisaresultinthecache,wedonotstartalong-runningoperationbutusethecachedvalueinstead.
Asimplecodeofacacheproviderthatcontainsonelong-runningoperationandimplementscacheasidepatternwilllookasfollows:
publicclassCustomProvider
{
privatereadonlyDictionary<string,OperationResult>_cache=newDictionary<string,OperationResult>();
privatereadonlyReaderWriterLockSlim_rwLockSlim=
newReaderWriterLockSlim();
publicOperationResultRunOperationOrGetFromCache(
stringoperationId)
{
_rwLockSlim.EnterReadLock();
try
{
OperationResultresult;
if(_cache.TryGetValue(operationId,outresult))
returnresult;
}
finally
{
_rwLockSlim.ExitReadLock();
}
www.EBooksWorld.ir
![Page 200: Mastering C# Concurrency - EBooksWorld...a print book customer, you are entitled to a discount on the eBook copy. Get in touch with us at for more details](https://reader035.vdocument.in/reader035/viewer/2022062917/5ed62e6504e9cb4adb670aed/html5/thumbnails/200.jpg)
_rwLockSlim.EnterWriteLock();
try
{
OperationResultresult;
if(_cache.TryGetValue(operationId,outresult))
returnresult;
result=RunLongRunningOperation(operationId);
_cache.Add(operationId,result);
returnresult;
}
finally
{
_rwLockSlim.ExitReadLock();
}
}
privateOperationResultRunLongRunningOperation(
stringoperationId)
{
//Runningreallong-runningoperation
//...
}
}
NoteItisveryimportanttoalwaysimplementacacheinvalidationstrategy,whichismissinginthisdemocodeasitisnotrelevanttothetopicofthechapter.However,inreal-worldscenarios,youhavetopayattentiontothistoavoidmemoryleaks.Thesimpleinvalidationstrategycanbesettingacacheitemlifetimeexplicitlyorusingweakreferencessothatgarbagecollectionwillinvalidatethecache.
ThissampledemonstratesaCustomProviderclass,whichcontainsonlyoneRunOperationOrGetFromCachepublicmethod.ThismethodacceptsanoperationidentifierandreturnstheoperationresultasanOperationResultobject.Toimplementcorrectcacheparallelreading,inthebeginningweacquireareaderlockandthencheckthatthereisaresultinthecache.Ifnot,weacquireawriterlockandthencheckthatthereisanoperationvalueinsidethecache,whichcanappearwhileweareacquiringthelock.Ifthereisstillnothinginthecache,wewillrunthelong-runningoperation,putitsresultintothecache,andreturnittotheclient.
Ifwedon’tperformthischeck,wecangetArgumentExceptionwhentryingtoaddanitemwiththesamekeytothedictionarytwice,andasaresultwedounnecessarywork.
TipHowever,asitusuallyhappensinconcurrentprogramming,thisapproachcanbenon-effectiveindifferentsituations.UsingReaderWriterLockSlimforimplementingdictionary-basedcachingalmostalwaysleadtoworseperformancethansimplyusingacommonstatement,lock(syncRoot).Theproblemisthatacquiringreaderlockisnotaveryfastoperation.AReaderWriterLockSlimobjecthastoensurethatacquiringawriter
www.EBooksWorld.ir
![Page 201: Mastering C# Concurrency - EBooksWorld...a print book customer, you are entitled to a discount on the eBook copy. Get in touch with us at for more details](https://reader035.vdocument.in/reader035/viewer/2022062917/5ed62e6504e9cb4adb670aed/html5/thumbnails/201.jpg)
lockisnotpossiblewhilebeinginsideareaderblock,andthisrequirestheuseofsomesynchronizationlogic,whichiscostly.Ifalongrunningoperationisreallylongrunning,thisoverheadisnotsignificant.However,inourcase,readingavaluefromDictionaryisaveryfastoperation,andinthissituation,lockingtheoverheadbecomesnoticeable.Sincealockstatementusesspin-waitoptimizationforshortrunningoperations,itwillbemoreeffectiveinthisparticularcase.
Theprevioustipworksforchoosingadatastructureaswell.Insimplecases,implementinggenerallockingovernonthreadsafeobjectcouldworkbetterthanaspecializeduniversalthreadsafedatastructure.However,whenconcurrentprogramlogicbecomesmorecomplicated,itisagoodideatogoforstandardconcurrentdatastructures.
www.EBooksWorld.ir
![Page 202: Mastering C# Concurrency - EBooksWorld...a print book customer, you are entitled to a discount on the eBook copy. Get in touch with us at for more details](https://reader035.vdocument.in/reader035/viewer/2022062917/5ed62e6504e9cb4adb670aed/html5/thumbnails/202.jpg)
www.EBooksWorld.ir
![Page 203: Mastering C# Concurrency - EBooksWorld...a print book customer, you are entitled to a discount on the eBook copy. Get in touch with us at for more details](https://reader035.vdocument.in/reader035/viewer/2022062917/5ed62e6504e9cb4adb670aed/html5/thumbnails/203.jpg)
Concurrentcollectionsin.NETSincethefirst.NETFrameworkversion,mostofthecollectionsintheSystem.CollectionsnamespacecontainedtheSynchronizedfactorymethodthatcreatesathreadsafewrapperoverthecollectioninstance,whichensuresthreadsafety:
varsource=Enumerable.Range(1,42000).ToList();
vardestination=ArrayList.Synchronized(newList<int>());
Parallel.ForEach(source,
n=>
{
destination.Add(n);
});
Assert.AreEqual(source.Count,destination.Count);
Thesynchronizedcollectionwrappercanbeusedinaconcurrentenvironment,butitsefficiencyislow,sinceitusessimplelockingensuringexclusivecollectionaccessforeveryoperation.Thisapproachiscalledcoarse-grainedlockinganditisdescribedinChapter3,UnderstandingParallelismGranularity.Itdoesnotscalewellwithanincreaseinthenumberofclientsandtheamountofdatainsidethecollection.
Acomplicated,butanefficient,approachistousefine-grainedlocking,sowecanprovideanexclusiveaccessonlytothepartsofthecollectionthatareinuse.Forexample,iftheunderlyingdatastorageisanarray,wecancreatemultiplelocksthatwillcoverthecorrespondingarrayparts.Thisapproachrequiresdeterminingtherequiredlockfirst,butitwillalsoallowanon-blockingaccesstothedifferentpartsofthearray.Thiswilluselocksonlywhenthereisaconcurrentaccesstothesamedata.Incertainscenarios,theperformancedifferencewillbehuge.
NotePLINQusesexactlythesameapproachforparallelcollectionsprocessing.Thereisaspecialmechanismcalledpartitioning,whichsplitsacollectioninmultiplesegments.Eachsegmentgetsprocessedonaseparatethread.AstandardpartitionerimplementationresidesinsidetheSystem.Collections.Concurrent.Partitionertype.
Withthe.NETFramework4.0release,anewsetofconcurrentcollectionsareavailablefor.NETdevelopers.Thesecollectionsarespecificallydesignedforhighloadconcurrentaccessanduselock-freeandfine-grainedapproachesinternally.ThesecollectionsareavailableintheSystem.Collections.Concurrentnamespace:
ConcurrentCollection System.Collections.Genericanalogue
ConcurrentDictionary<TKey,TValue> Dictionary<TKey,TValue>
ConcurrentBag<T> None
ConcurrentQueue<T> Queue<T>
www.EBooksWorld.ir
![Page 204: Mastering C# Concurrency - EBooksWorld...a print book customer, you are entitled to a discount on the eBook copy. Get in touch with us at for more details](https://reader035.vdocument.in/reader035/viewer/2022062917/5ed62e6504e9cb4adb670aed/html5/thumbnails/204.jpg)
ConcurrentStack<T> Stack<T>
Eachoftheseconcurrentcollectionsaresuitablefordifferentworkscenarios.Further,wewillgothroughallofthesedatastructuresandreviewtheimplementationdetailsandthebest-suitedworkscenario.
www.EBooksWorld.ir
![Page 205: Mastering C# Concurrency - EBooksWorld...a print book customer, you are entitled to a discount on the eBook copy. Get in touch with us at for more details](https://reader035.vdocument.in/reader035/viewer/2022062917/5ed62e6504e9cb4adb670aed/html5/thumbnails/205.jpg)
www.EBooksWorld.ir
![Page 206: Mastering C# Concurrency - EBooksWorld...a print book customer, you are entitled to a discount on the eBook copy. Get in touch with us at for more details](https://reader035.vdocument.in/reader035/viewer/2022062917/5ed62e6504e9cb4adb670aed/html5/thumbnails/206.jpg)
ConcurrentDictionaryWecanimprovetheimplementationofCustomProviderusingConcurrentDictionary<TKey,TValue>tohandlethesynchronization:
publicclassCustomProvider
{
privatereadonly
ConcurrentDictionary<string,OperationResult>_cache=
newConcurrentDictionary<string,OperationResult>();
publicOperationResultRunOperationOrGetFromCache(
stringoperationId)
{
return_cache.GetOrAdd(operationId,
id=>RunLongRunningOperation(id));
}
privateOperationResultRunLongRunningOperation(
stringoperationId)
{
//Runningreallong-runningoperation
//...
Console.WriteLine("Runninglong-runningoperation");
returnOperationResult.Create(operationId);
}
}
Thecodebecamemuchsimpler.WejustusedtheGetOrAddmethodanditdoesexactlywhatweneed;ifthereisanelementinthedictionary,itjustreturnsitsvalueorrunsaprovideddelegate,getstheresultvalue,andstoresitinthedictionary.
Everyconcurrentcollectionimplementsacorrespondinggenericinterface.Forexample,ConcurrentDictionary<TKey,TValue>implementsthestandardIDictionary<TKey,TValue>interface.Howeverbesidesthis,itintroducesnewmethodsbecauseitisnotenoughtointroducethethreadsafeversionofeachmethod.Considerthisexample:
privatereadonlyIDictionary<string,OperationResult>_cache=
newConcurrentDictionary<string,OperationResult>();
publicOperationResultRunOperationOrGetFromCache(
stringoperationId)
{
OperationResultresult;
if(_cache.TryGetValue(operationId,outresult))
{
returnresult;
}
result=RunLongRunningOperation(operationId);
_cache.Add(operationId,result);
returnresult;
}
www.EBooksWorld.ir
![Page 207: Mastering C# Concurrency - EBooksWorld...a print book customer, you are entitled to a discount on the eBook copy. Get in touch with us at for more details](https://reader035.vdocument.in/reader035/viewer/2022062917/5ed62e6504e9cb4adb670aed/html5/thumbnails/207.jpg)
Thiscodewillnotworkcorrectlyinamultithreadedenvironment.BoththeTryGetValueandAddoperationsarethreadsafe,butasequenceoftwooperationswithoutadditionalsynchronizationcancausearacecondition,andinthisexample,itispossibletogetanexceptionthrownfromtheAddmethodwhiletryingtoaddanelementwhenithasalreadybeenaddedtothedictionarybyanotherthread.
Itisclearthatinthissituation,justhavingtheIDictionary<TKey,TValue>implementationisnotenough.Oneofthepossiblesolutionsistoreplace_cache.Addwiththe_cache.TryAddmethod,butthiswillrequireustogetbacktousingaconcreteclass:
privatereadonly
ConcurrentDictionary<string,OperationResult>_cache=
newConcurrentDictionary<string,OperationResult>();
publicOperationResultRunOperationOrGetFromCache(
stringoperationId)
{
OperationResultresult;
if(_cache.TryGetValue(operationId,outresult))
{
returnresult;
}
result=RunLongRunningOperation(operationId);
_cache.TryAdd(operationId,result);
returnresult;
}
Whilethissolutionisalsofarfromperfect,wecanalreadyseewhyconcurrentcollectionschangedthecommonAPIandintroducedasetofnewmethods.Usually,thesenewmethodsrepresentatomicoperationsthatconsistofseveralstepsandeachstepperformsaspecificactioninternally:GetOrAdd,AddOrUpdate,andsoon.
Nowlet’sreviewonemoreimportantaspectofthisimplementation.Ifwelookatthecodethoroughly,wecanseethatdespitetherebeingnoerrorsintheconcurrentenvironmentitispossiblethattheRunLongRunningOperationmethodcanbecalledtwice.Thus,onlythefirstresultwillbestoredinthedictionaryandthelattermethodcallresultwillbewasted.ThisisalsoimportantbecausetheGetOrAddmethodoftheConcurrentDictionary<TKey,TValue>classisimplementedinaverysimilarway.
ThismeansthatusingRunOperationOrGetFromCacheinaconcurrentenvironmentwillresultincallingalongrunningoperationmultipletimesperonevalue.Ifthisturnsouttobecostly,similartotransmittingalargevolumeofdataviathenetworkorperformingCPUintensivelongtimecalculations,thisisdefinitelynotagoodapproach.
www.EBooksWorld.ir
![Page 208: Mastering C# Concurrency - EBooksWorld...a print book customer, you are entitled to a discount on the eBook copy. Get in touch with us at for more details](https://reader035.vdocument.in/reader035/viewer/2022062917/5ed62e6504e9cb4adb670aed/html5/thumbnails/208.jpg)
UsingLazy<T>SinceAddOrGetisimplementedinawaythateverycalltothismethodwiththesamekeywillresultingettingthesamevalue,wecanusealittletricktopreventthelongrunningoperationfromrunningmultipletimes:
privatereadonly
ConcurrentDictionary<string,Lazy<OperationResult>>_cache=
newConcurrentDictionary<string,Lazy<OperationResult>>();
publicOperationResultRunOperationOrGetFromCache(
stringoperationId)
{
return_cache.GetOrAdd(operationId,
id=>newLazy<OperationResult>(
()=>RunLongRunningOperation(id))).Value;
}
Inthisexample,wewraptheRunLongRunningOperationmethodcallintoaspecialobject—Lazy<OperationResult>.Thisclassisapartofthe.NETFrameworkBaseClassLibrary(BCL)thatensuresthattheprovideddelegatewillbeexecutedonlyonceandonlywhenitsValuepropertyisaccessedbyanexternalcode.
WecanlookattheGetOrAddmethodimplementationdetailstofullyunderstandwhatishappeningunderthehood:
//ConcurrentDictionary<TKey,TValue>implementation
publicTValueGetOrAdd(TKeykey,Func<TKey,TValue>valueFactory)
{
TValueresultingValue;
if(TryGetValue(key,outresultingValue))
{
returnresultingValue;
}
TryAddInternal(key,valueFactory(key),false,true,
outresultingValue);
returnresultingValue;
}
///<summary>
///Sharedinternalimplementationforinsertsandupdates.
///Ifkeyexists,wealwaysreturnfalse;
///andifupdateIfExists==truewe
///forceupdatewithvalue;
///Ifkeydoesn'texist,wealwaysaddvalueandreturntrue;
///</summary>
privateboolTryAddInternal(TKeykey,TValuevalue,
boolupdateIfExists,boolacquireLock,
outTValueresultingValue)
{
//...Theimplementationdetails
}
www.EBooksWorld.ir
![Page 209: Mastering C# Concurrency - EBooksWorld...a print book customer, you are entitled to a discount on the eBook copy. Get in touch with us at for more details](https://reader035.vdocument.in/reader035/viewer/2022062917/5ed62e6504e9cb4adb670aed/html5/thumbnails/209.jpg)
Note.NETFrameworkCoreisnowopensourceandcanbefoundonGitHubintheMicrosoft/dotnetrepository.However,thereisamoreconvenientwaytolearnthe.NETsourcecode—areferencesource.microsoft.comwebsite.Thisresourcewasspecificallycreatedforlearningtheinternalsof.NETandprovidesacomfortablesearchandnavigationusingthecodesemantics,notjustasimpletextsearch.Forexample,ifyouarelookingforallthecasesoftheSystem.String.Substring(System.Int32)methodusage,youwillnotgetanyotherSubstringmethodoverloads.
Wecanseethatifthereisnocachedoperationresultinthedictionary,weimmediatelycallvalueFactory(key)(thisiswheremultipleRunLongRunningOperationcallshappen),andthereturnedresultgoestotheTryAddInternalmethod.EventhecommentstothismethodstatethatifakeyexistsandtheupdateIfExistsparameterequalstofalse,wewillusetheoldvaluethathasbeenalreadystoredinthedictionary.
UsingLazy<OperationResult>insteadofOperationResultleadstoasituationwherewecallonlytheLazy<T>objectconstructormultipletimes,whilealongrunningoperationwillbeexecutedonlyoncewhenthefirstGetOrAddmethodcallcompletes.
www.EBooksWorld.ir
![Page 210: Mastering C# Concurrency - EBooksWorld...a print book customer, you are entitled to a discount on the eBook copy. Get in touch with us at for more details](https://reader035.vdocument.in/reader035/viewer/2022062917/5ed62e6504e9cb4adb670aed/html5/thumbnails/210.jpg)
ImplementationdetailsConcurrentDictionaryisinfactausualhashtablethatcontainsanarrayofbucketsprotectedbyanarrayoflocks.Thenumberoflockscanbedefinedbytheuserandtheoretically,allowsmanythreadstoaccessthedictionarywithoutanycontentioniftheyallusedifferentlocksandthus,thedifferentpartsofdatainthedictionary.
AConcurrentDictionaryinnerstructureschemelookslikethis:
TheentireConcurrentDictionarystateisplacedinaseparateTablesclassinstanceinthem_tablesfield.Thismakesitpossibletohaveanatomicstatechangeoperationforthedictionarywiththehelpofthecompare-and-swap(CAS)operations.
TheTablesclasscontainsthefollowingmostimportantfields:
m_buckets:Thisisanarrayofbuckets;eachofthebucketscontainsasingly-linkedlistofnodeswithdictionarydata.m_locks:Thisisanarrayoflocks;eachlockprovidessynchronizedaccesstooneormorebuckets.m_countPerLock:Thisisanarrayofcounters;eachcountercontainsatotalnumberofnodesthatareprotectedbythecorrespondinglock.Forexample,ifwelookatthepreviousscheme,wherethefirstlockprotectsthefirsttwobuckets,them_countPerLock[0]elementwillcontainthevalueof5.m_comparer:ThisisanIEqualityComparer<TKey>objectthatcontainsthelogicforcalculatingthehashvalueofakeyobject.
TheConcurrentDictionaryclassinturncontainsthreelargeoperationsgroups:
Lock-freeoperations:ThiskindofoperationcanberuninparallelfrommultiplethreadswithoutanycontentionFine-grainedlockoperations:Asithasbeenalreadyexplained,theseoperationscanbeconcurrentlyexecutedwithoutanycontentioniftheymanipulatethedifferentpartsofdatainsidethedictionaryExclusivelockoperations:Theseoperationscanrunonlyonasinglethreadandrequireafullcollectionlocktoensurethreadsafety
Lock-freeoperations
www.EBooksWorld.ir
![Page 211: Mastering C# Concurrency - EBooksWorld...a print book customer, you are entitled to a discount on the eBook copy. Get in touch with us at for more details](https://reader035.vdocument.in/reader035/viewer/2022062917/5ed62e6504e9cb4adb670aed/html5/thumbnails/211.jpg)
Theseoperationsdonotrequireanylockandcanbeusedsafelyfrommultiplethreads.Thisisthelistofthecorrespondingmethods:
ContainsKey
TryGetValue
ReadaccessbydictionaryindexerGetEnumerator
ThefirstthreeoperationsarebasedontheTryGetValuemethod.Thiscontainsthefollowingsteps:
1. Getthekeyobjecthashcodeusingcurrentcomparer.2. GetthebucketnumberbythekeyhashwiththehelpoftheGetBucketAndLockNo
method.Thelocknumberisnotusedatthemoment.3. Iterateoverthecurrentbucketnodelisttofindthecorrespondingvalue:
publicboolTryGetValue(TKeykey,outTValuevalue)
{
intbucketNo,lockNoUnused;
Tablestables=m_tables;
GetBucketAndLockNo(
tables.m_comparer.GetHashCode(key),outbucketNo,out
lockNoUnused,
tables.m_buckets.Length,tables.m_locks.Length);
//TheVolatile.Readensuresthattheloadofthe
//fieldsof'n'doesn'tmovebeforetheloadfrombuckets[i].
Noden=Volatile.Read<Node>(reftables.m_buckets[bucketNo]);
//IterateoverNodestofindentrywithacorrespondingkey
...
}
TheGetEnumeratormethodimplementationisquitestraightforward:
publicIEnumerator<KeyValuePair<TKey,TValue>>GetEnumerator()
{
Node[]buckets=m_tables.m_buckets;
for(inti=0;i<buckets.Length;i++)
{
//TheVolatile.Readensuresthat
//theloadofthefieldsof'current'
//doesn'tmovebeforetheloadfrombuckets[i].
Nodecurrent=Volatile.Read<Node>(refbuckets[i]);
while(current!=null)
{
yieldreturnnewKeyValuePair<TKey,TValue>(
current.m_key,current.m_value);
current=current.m_next;
}
www.EBooksWorld.ir
![Page 212: Mastering C# Concurrency - EBooksWorld...a print book customer, you are entitled to a discount on the eBook copy. Get in touch with us at for more details](https://reader035.vdocument.in/reader035/viewer/2022062917/5ed62e6504e9cb4adb670aed/html5/thumbnails/212.jpg)
}
}
Aswecansee,theGetEnumeratormethoddoesnotcreateacopyofbucketscontents,andthisallowsmultiplethreadstochangethedictionarydatawhileanotherthreaditeratesovertheseelements.NoticetheVolatile.Readconstructthatcreatesanacquire-fenceandensuresthatnoreadsorwritescanbereorderedbeforetheloadfrombuckets[i].
Fine-grainedlockoperationsTheseoperationsusuallyworkwithasingleelementinsidethedictionary.Thesemethodsusethefine-grainedlockingapproach:
TryAdd
TryRemove
TryUpdate
writeaccessbyadictionaryindexerGetOrAdd
AddOrUpdate
TheseoperationsinternallyusetheGetBucketAndLockNomethod,whichreturnsthebucketandthelocknumbers.Theimplementationusuallycontainsthefollowingsteps:
1. Getthekeyobjecthashcode.2. Getthebucketandthelocknumbers.3. Acquirethelock.4. Changethecurrentbucket—deleteorchangesomeelementinside.5. Releasetheacquiredlock.
MostoftheoperationsintheprecedinglistusetheTryAddInternalmethodinternally.Let’sreviewthesimplifiedcodeofthismethod:
privateboolTryAddInternal(TKeykey,TValuevalue,
outTValueresultingValue)
{
while(true)
{
boolresizeDesired=false;
vartables=m_tables;
intbucketNo,lockNo;
inthashcode=tables.m_comparer.GetHashCode(key);
GetBucketAndLockNo(hashcode,outbucketNo,outlockNo);
try
{
Monitor.Enter(tables.m_locks[lockNo]);
//Ifthetablejustgotresized,wemaynotbeholding
//therightlock,andmustretry.
//Thisshouldbearareoccurence.
if(tables!=m_tables)
{
www.EBooksWorld.ir
![Page 213: Mastering C# Concurrency - EBooksWorld...a print book customer, you are entitled to a discount on the eBook copy. Get in touch with us at for more details](https://reader035.vdocument.in/reader035/viewer/2022062917/5ed62e6504e9cb4adb670aed/html5/thumbnails/213.jpg)
continue;
}
//LoopingthroughNodesinthebucket.
//IfexistingNodewasfound
//themethodreturnsfalse,otherwise
//newNodewouldbeadded
for(Nodenode=tables.m_buckets[bucketNo];
node!=null;node=node.m_next)
{
//...
}
//Ifthenumberofelementsguardedbythislockhas
//exceededthebudget,resizethebuckettable.
//ItisalsopossiblethatGrowTablewillincrease
//thebudgetbutwon'tresizethebuckettable.
//Thathappensifthebuckettableisfoundtobe
//poorlyutilizedduetoabadhashfunction.
if(tables.m_countPerLock[lockNo]>m_budget)
{
resizeDesired=true;
}
}
finally
{
Monitor.Exit(tables.m_locks[lockNo]);
}
//Resizetableifneeded.
//Thismethodshouldbecalledoutsidethelock
//topreventadeadlocks.
if(resizeDesired)
{
GrowTable(tables,tables.m_comparer);
}
resultingValue=value;
returntrue;
}
}
Itisclearthatthiscodeimplementsalltheprecedingsteps—wegetthekeyhash,thebucket,andthelocknumberandproceedtotheelementneeded.However,thereareacoupleofimportantpointstopayattentionto:
Usingthewhilelooptoworkaroundthesituationwhereanotherthreadhaschangedthecollectionanditsm_tablesfield.Inthiscase,wejustretryuntilwesucceedandtheoldandnewm_tablesvaluesremainequal.Whennodecountperonelockexceedssomethresholdvalue(m_budget),thehashtablerebalancingoccursinsidetheGrowTablemethod.Thisrequiresanexclusivelockforthedictionarytobeacquired.
Exclusivelockoperations
www.EBooksWorld.ir
![Page 214: Mastering C# Concurrency - EBooksWorld...a print book customer, you are entitled to a discount on the eBook copy. Get in touch with us at for more details](https://reader035.vdocument.in/reader035/viewer/2022062917/5ed62e6504e9cb4adb670aed/html5/thumbnails/214.jpg)
TherearemoreoperationsthatarerequiredtogetanexclusivelockastheGrowTabledoes.Itisveryimportanttoknowtheseoperationsandavoidusingtheminamultithreadedenvironmentifpossible.Hereistheoperationslist:
Clear
ToArray
CopyTo
Count
IsEmpty
GetKeys
GetValues
Werememberthattryingtoworkwithmultiplelockscaneasilyleadtodeadlocksinaconcurrentprogram.Fortunately,theconcurrentdictionarycontainstheAcquireLocksmethodthatcansafelyacquiremultiplelocksalwaysinthesameorderthatpreventsdeadlocks.ThismethodisusedinternallyfromtheAcquireAllLocksmethod,whichsafelyacquiresallthelocksinthedictionary.
Everyoperationlistedpreviouslyusesthesamealgorithm;first,itcallsAcquireAllLockstopreventconcurrentchangestothedictionary,thenitmodifiesthem_tableinstanceandchangesthedictionarystate.Forexample,hereishowtheCountpropertyisimplemented:
publicintCount
{
get
{
intcount=0;
try
{
//Acquirealllocks
AcquireAllLocks();
//Computethecount,weallowoverflow
for(inti=0;i<m_tables.m_countPerLock.Length;i++)
{
count+=m_tables.m_countPerLock[i];
}
}
finally
{
//Releaselocksthathavebeenacquiredearlier
ReleaseLocks();
}
returncount;
}
}
www.EBooksWorld.ir
![Page 215: Mastering C# Concurrency - EBooksWorld...a print book customer, you are entitled to a discount on the eBook copy. Get in touch with us at for more details](https://reader035.vdocument.in/reader035/viewer/2022062917/5ed62e6504e9cb4adb670aed/html5/thumbnails/215.jpg)
UsingtheimplementationdetailsinpracticeKnowingtheprinciplesofhowtheconcurrentdictionaryisimplementedcanhelpyouinsomepracticalsituations.
Abetterunderstandingofconcurrentdictionaryconstructorparameters,forexample,concurrencyLevel,willhelptotuneupyourdatastructurefortheconcretetask.Ononehand,themorelockswecreate,themorethreadscanpotentiallyworkwiththedictionarywithoutlocking,whichisagoodthing.Ontheotherhand,creatingmorelockscreatesmoreperformanceoverhead,andwecannotexplicitlysetalockcontrolorabucket,sothiscanleadtodeclineofperformance.Knowingthesedetailswillhelpustostudytheprogramunderaprofilertofindthebestsolutionforourconcretecase.
Anotherimportantimplementationaspectisthedictionarybucketscontainingsingly-linkedlists.AddinganelementtosuchalistisanO(N)operationandthiscanbeaproblemwhenstoringhundredsofthousandsofsmallitemsinthedictionary.
SincetheCount,ToArray,andIsEmptyoperationsrequireexclusivelocking,insomecasesusingcorrespondingLINQalternativessuchasEnumerable.Count(),Enumerable.ToArray(),andEnumerable.Any()willbemuchmoreefficientinsituationswherethedictionaryoftengetsconcurrentlyupdated.
www.EBooksWorld.ir
![Page 216: Mastering C# Concurrency - EBooksWorld...a print book customer, you are entitled to a discount on the eBook copy. Get in touch with us at for more details](https://reader035.vdocument.in/reader035/viewer/2022062917/5ed62e6504e9cb4adb670aed/html5/thumbnails/216.jpg)
www.EBooksWorld.ir
![Page 217: Mastering C# Concurrency - EBooksWorld...a print book customer, you are entitled to a discount on the eBook copy. Get in touch with us at for more details](https://reader035.vdocument.in/reader035/viewer/2022062917/5ed62e6504e9cb4adb670aed/html5/thumbnails/217.jpg)
ConcurrentBag<T>ConcurrentBag<T>isoneofthesimplestconcurrentcollections.Itisintendedtostoreanygeneral-purposedata.Themainfeatureofthiscollectionishowitstoresthedata;theAddmethodappendsanitemtoadoubly-linkedlistthatisstoredinthecurrentthread’slocalstorage.Thismakestheappendingoperationveryefficient,sincethereisnocontention.GettinganitemfromthecollectionwiththeTryTakeorTryPeekmethodsisalsoquiteefficient.First,welookfortheiteminthelocallist,butifitisempty,welookforitemsinotherthreads’locallists.
Thisapproachiscalledworkstealingandworkswellwheneachthreadcontainsmoreorlessthesamenumberofdataandusesthesamenumberofappendandtakeoperations.
Let’sreviewanexampleofusingtheConcurrentBag<T>datastructure:
varbag=newConcurrentBag<string>();
vartask1=Run(()=>
{
AddAndPrint(bag,"[T1]:Item1");
AddAndPrint(bag,"[T1]:Item2");
AddAndPrint(bag,"[T1]:Item3");
Thread.Sleep(2000);
TakeAndPrint(bag);
TakeAndPrint(bag);
},threadName:"T1");
vartask2=Run(()=>
{
AddAndPrint(bag,"[T2]:Item1");
AddAndPrint(bag,"[T2]:Item2");
AddAndPrint(bag,"[T2]:Item3");
Thread.Sleep(1000);
TakeAndPrint(bag);
TakeAndPrint(bag);
TakeAndPrint(bag);
TakeAndPrint(bag);
},threadName:"T2");
Task.WaitAll(task1,task2);
TheAddAndPrint,TakeAndPrintandRunmethodshelptocreateathreadwithagivennameandallowsustoappendandremoveelementsfromtheConcurrentBag<T>object,whileprintingtheelementvaluetotheconsole:
privatestaticTaskRun(Actionaction,stringthreadName)
{
vartcs=newTaskCompletionSource<object>();
varthread=newThread(()=>
{
action();
tcs.SetResult(null);
www.EBooksWorld.ir
![Page 218: Mastering C# Concurrency - EBooksWorld...a print book customer, you are entitled to a discount on the eBook copy. Get in touch with us at for more details](https://reader035.vdocument.in/reader035/viewer/2022062917/5ed62e6504e9cb4adb670aed/html5/thumbnails/218.jpg)
});
thread.Name=threadName;
thread.Start();
returntcs.Task;
}
privatestaticvoidAddAndPrint(ConcurrentBag<string>bag,
stringvalue)
{
Console.WriteLine("{0}:Add-{1}",
Thread.CurrentThread.Name,value);
bag.Add(value);
}
privatestaticvoidTakeAndPrint(ConcurrentBag<string>bag)
{
stringvalue;
if(bag.TryTake(outvalue))
{
Console.WriteLine("{0}:Take-{1}",
Thread.CurrentThread.Name,value);
}
}
Herewecreatedtwotasks,andeachtasksetstwoelementstothequeue.Thenitwaitsforsometimeandstartstoprocesstheappendedelements.TheinnerstoragestructureoftheConcurrentBagobjectwilllooklikethiswhentheappendingoftheelementsisfinished:
ConcurentBag<T>,aswehavealreadymentioned,containsseveraldoubly-linkedlists,
www.EBooksWorld.ir
![Page 219: Mastering C# Concurrency - EBooksWorld...a print book customer, you are entitled to a discount on the eBook copy. Get in touch with us at for more details](https://reader035.vdocument.in/reader035/viewer/2022062917/5ed62e6504e9cb4adb670aed/html5/thumbnails/219.jpg)
onelistforeachthread.Addinganitemleadstoappendingittotheendofthelocallist,butgettingitemsfromtheconcurrentbagisslightlymorecomplicated:
T2:Add-[T2]:Item1
T1:Add-[T1]:Item1
T2:Add-[T2]:Item2
T2:Add-[T2]:Item3
T1:Add-[T1]:Item2
T1:Add-[T1]:Item3
T2:Take-[T2]:Item3
T2:Take-[T2]:Item2
T2:Take-[T2]:Item1
T2:Take-[T1]:Item1
T1:Take-[T1]:Item3
T1:Take-[T1]:Item2
Weappenditemstothecollectionfromtwothreads,andthisexplainsanadditionorderthatwasdemonstratedpreviously.ThemostinterestingthingishowitemsareremovedfromConcurrentBag.Inourcase,thesecondthreadstartsgettingtheitemsfromthecollection.First,itgetstheelementsthatwereaddedbythisthread,butinthereverseorder(fromtheendofthedoubly-linkedlist).Whenthelocallistbecomesempty,ittriesto“steal”workfromanotherthread,butthistimeitgetsitemsfromthebeginningoftheunderlyinglist.
www.EBooksWorld.ir
![Page 220: Mastering C# Concurrency - EBooksWorld...a print book customer, you are entitled to a discount on the eBook copy. Get in touch with us at for more details](https://reader035.vdocument.in/reader035/viewer/2022062917/5ed62e6504e9cb4adb670aed/html5/thumbnails/220.jpg)
ConcurrentBaginpracticeTheimplementationdetailsoftheConcurentBag<T>datastructuremakesitusefulonlyinveryspecificscenarios.Readingandwritingobjectshastohappenonthesamethreadtominimizecontention.Itmakesthiscollectionnotveryusefulinmostcommonsituations,sinceusuallydifferentthreadsappendandreaddatafromacollection.
AgoodpracticalscenarioforConcurentBag<T>isanobjectpool.Itisusuallyimplementedinawaythatwhensomeobject,whichissignificantlyexpensivetocreate,doesnotgetcleanedupbythegarbagecollector,itgoestosomeobjectstorageandiseasilyaccessedwhenneeded.Sinceusuallysuchoperationshappenonasinglethread,thiswillmakeaperfectconditiontousethiskindofconcurrentcollection.
Anothersimilarexampleisathreadpoolimplementation.IfwelookcloselyattheDefaultTaskSchedulerimplementationfromTaskParallelLibrary,wecanseethatithasthesamebehaviorastheconcurrentbag.Thistaskschedulerdoesnotuseaglobaltasklist;instead,itcreatesanumberoflocaltasklistsforeachworkerthread.Ifsometaskcreatesachildtask(withoutprovidingthePreferFairnessoption),itwillbeappendedtothelocaltasklist.ThishelpstoreducecontentionandhasahigherprobabilityoffindingtherequireddataintheCPUcache.Alsoitusesworkstealingincasethelocaltasklistisempty.
However,eveniftheconcurrentbagperfectlyfitsinyourscenario,itisagoodideatotrytouseotherdatastructuresandmeasureandcomparetheperformanceofeachimplementation.Thesynthetictests(theycanbefoundinthecodesamplesofthischapter)showthattheConcurrentBag<T>performanceisnotimpressive,andmaybechoosingConcurrentQueue<T>orConcurrentStack<T>willbeabettersolution.Eveninperfectconditionswhenthesamethreadappendsandretrievesdata,aconcurrentbagisaboutthreetimesslowerthanaconcurrentqueue.
www.EBooksWorld.ir
![Page 221: Mastering C# Concurrency - EBooksWorld...a print book customer, you are entitled to a discount on the eBook copy. Get in touch with us at for more details](https://reader035.vdocument.in/reader035/viewer/2022062917/5ed62e6504e9cb4adb670aed/html5/thumbnails/221.jpg)
www.EBooksWorld.ir
![Page 222: Mastering C# Concurrency - EBooksWorld...a print book customer, you are entitled to a discount on the eBook copy. Get in touch with us at for more details](https://reader035.vdocument.in/reader035/viewer/2022062917/5ed62e6504e9cb4adb670aed/html5/thumbnails/222.jpg)
ConcurrentQueue<T>ConcurrentQueue<T>isaconcurrentversionoftheQueue<T>class.Itcontainsthreebasicmethods:Enqueueappendsanitemtothequeue,TryDequeueretrievesanitemfromthequeueifitispossible,andTryPeekgetsthefirstelementinthequeuewithoutremovingitfromthequeue.Thelasttwomethodsreturnfalseifthequeueisempty.
Nowlet’sseeasamplecodeforConcurrentQueue<T>:
varqueue=newConcurrentQueue<string>();
vartask1=Run(()=>
{
AddAndPrint(queue,"[T1]:Item1");
AddAndPrint(queue,"[T1]:Item2");
AddAndPrint(queue,"[T1]:Item3");
Thread.Sleep(2000);
TakeAndPrint(queue);
TakeAndPrint(queue);
},threadName:"T1");
vartask2=Run(()=>
{
AddAndPrint(queue,"[T2]:Item1");
AddAndPrint(queue,"[T2]:Item2");
AddAndPrint(queue,"[T2]:Item3");
Thread.Sleep(1000);
TakeAndPrint(queue);
TakeAndPrint(queue);
TakeAndPrint(queue);
TakeAndPrint(queue);
},threadName:"T2");
Task.WaitAll(task1,task2);
Inthisexample,wedothesamewiththeConcurrentBag<T>code.Wecreatetwonamedthreads;eachthreadappendsthreeitemstothequeue.Thenaftersomepause,threadsstarttoretrievetheelementsfromthequeue:
T1:Add-[T1]:Item1
T2:Add-[T2]:Item1
T2:Add-[T2]:Item2
T2:Add-[T2]:Item3
T1:Add-[T1]:Item2
T1:Add-[T1]:Item3
T2:Dequeue-[T1]:Item1
T2:Dequeue-[T2]:Item1
T2:Dequeue-[T2]:Item2
T2:Dequeue-[T2]:Item3
T1:Dequeue-[T1]:Item2
T1:Dequeue-[T1]:Item3
ConcurrentqueueisaFIFO(FirstIn,FirstOut)collection,butsincethisisa
www.EBooksWorld.ir
![Page 223: Mastering C# Concurrency - EBooksWorld...a print book customer, you are entitled to a discount on the eBook copy. Get in touch with us at for more details](https://reader035.vdocument.in/reader035/viewer/2022062917/5ed62e6504e9cb4adb670aed/html5/thumbnails/223.jpg)
multithreadedenvironment,theorderofappendingandremovingelementsisnotstrictlysequential.
TheConcurrentQueue<T>classisimplementedonasingly-linkedlistofringbuffers(orsegments).Thisallowsthiscollectiontobelock-freethatmakesitveryattractivetousethisinhighloadconcurrentapplications.
Inthebeginning,aconcurrentqueuecreatesonesegmentthatisreferencedbytwoinnerfields:m_headandm_tail(thefirstandthelastsegmentsreferencecorrespondingly).Thesegmentsizeis32bytes,andeachsegmentcontainstworeferences:LowandHigh.LowreferencesanelementpositioninthebufferthatcanberemovedbycallingtheDequeuemethod,andHighreferencesthelastiteminthebufferthathasbeenaddedbyusingtheEnqueuemethod.
Hereishowthequeuewilllookinternallyafterappendingsixelementsandthenremovingtwoofthem:
Ifwefindoutduringtheprocessofappendinganelementtothequeuethatthesegmentisfull,thenonemoresegmentiscreatedandattachedtotheendofthesegmentlist.Onlythefirstandthelastsegmentscanbepartiallyfull,everyothersegmentmustbecompletelyfull.
Ifweappend80elementsandthenremovefour,wewillseesomethinglikethis:
Theoverallqueuesizewillbe32–4+32+16=76.Thequeuewillcontainthreesegments,andthefirstandthelastsegmentswillbepartiallyfilled.
www.EBooksWorld.ir
![Page 224: Mastering C# Concurrency - EBooksWorld...a print book customer, you are entitled to a discount on the eBook copy. Get in touch with us at for more details](https://reader035.vdocument.in/reader035/viewer/2022062917/5ed62e6504e9cb4adb670aed/html5/thumbnails/224.jpg)
www.EBooksWorld.ir
![Page 225: Mastering C# Concurrency - EBooksWorld...a print book customer, you are entitled to a discount on the eBook copy. Get in touch with us at for more details](https://reader035.vdocument.in/reader035/viewer/2022062917/5ed62e6504e9cb4adb670aed/html5/thumbnails/225.jpg)
ConcurrentStack<T>TheConcurrentStack<T>datastructureisaconcurrentversionofastandardStack<T>collection.Itcontainsthreemainmethods:Push,TryPop,andTryPeek,toappend,retrieveandgettheitemfromthecollectionbyFILO(FirstIn,LastOut)principle.
ConcurrentStack<T>isimplementedasasingly-linkedlock-freelist,whichmakesitlessinterestingintermsofreviewingtheimplementationdetails.Nevertheless,itisstillusefultoknow,andifwehavetochooseaconcurrentdatastructureforascenariowhereelementsprocessingorderisnotimportant,itispreferabletouseaconcurrentqueuesinceithaslessperformanceoverhead.Appendingelementstotheconcurrentstackalwaysleadstoadditionalmemoryallocation,whichcanbeasignificantdrawbackincertainscenarios.
www.EBooksWorld.ir
![Page 226: Mastering C# Concurrency - EBooksWorld...a print book customer, you are entitled to a discount on the eBook copy. Get in touch with us at for more details](https://reader035.vdocument.in/reader035/viewer/2022062917/5ed62e6504e9cb4adb670aed/html5/thumbnails/226.jpg)
www.EBooksWorld.ir
![Page 227: Mastering C# Concurrency - EBooksWorld...a print book customer, you are entitled to a discount on the eBook copy. Get in touch with us at for more details](https://reader035.vdocument.in/reader035/viewer/2022062917/5ed62e6504e9cb4adb670aed/html5/thumbnails/227.jpg)
TheProducer/ConsumerpatternTheProducer/Consumerpatternisoneofthemostwidelyusedparallelprogrammingpatterns.Themostnaturalapproachistoorganizeyourapplicationforprocessingworkitemsonanotherthread.Inthiscase,wegettwoapplicationparts—oneputsnewworktobeprocessedandtheotherchecksfornewworkandperformselementprocessing.Thestandard.NETFrameworkthreadpoolisagoodexample;onethreadputsaworkiteminaprocessingqueuebycallingtheTask.RunfunctionoftheThreadPool.QueueUserWorkItemmethods,andtheinfrastructurefindsotherthreadstoprocessthesetasks.
NoteTheotherparallelprogrammingpatternswillbereviewedinthenextchapter.TheProducer/Consumerpatternisverytightlyrelatedtoconcurrentdatastructures,anditismorenaturallydescribedalongwiththem.
Anotherclassicexampleisauserinterfaceprogramming.TocreateresponsiveandfastUI,aUIthreadhastooffloadasmuchworkaspossibletootherthreads.Therefore,itpoststaskstoaqueue,andsomebackgroundthreadsprocessthesetasksandprovidetheresultbacktoUI.
Thesameapproachisusedinserver-sideprogramming.Toeffectivelyprocessclientrequests,theyarequeuedfirst,andonlythendoestheserverinfrastructureassignaworkerthreadtoprocesstheuserrequest.
www.EBooksWorld.ir
![Page 228: Mastering C# Concurrency - EBooksWorld...a print book customer, you are entitled to a discount on the eBook copy. Get in touch with us at for more details](https://reader035.vdocument.in/reader035/viewer/2022062917/5ed62e6504e9cb4adb670aed/html5/thumbnails/228.jpg)
CustomProducer/ConsumerpatternimplementationLet’strytoimplementtheProducer/ConsumerpatternwiththehelpofthestandardQueue<T>class.Beforewecangettotheprogramming,wehavetothinkabouttherequirements:
WhatshouldaconsumerdowhencallingtheTakemethodwhilethecurrentqueuedoesnotcontainanyelements?WhatshouldaproducerdowhencallingtheAddmethodasthecollectionsizehasreachedsomethresholdvalue?
Ifwelookatthestandardconcurrentcollectionsimplementation,itmakessensetoreplacetheTakemethodwithTryTake,andthiswillreturnfalseifthequeueisempty.InsteadoftheAddmethod,wecanimplementTryAddthatwillreturnfalsewhenthequeueisfull.Unfortunately,itisnotthebestdesignforaProducer/Consumerqueue.
AmorenaturalapproachwouldbetomaketheTakemethodblockthecurrentthreadwhentheunderlyingqueueisemptyandreturntheresultassoonasanyproducerthreadaddsanitemtothequeue;suchaqueueiscalledablockingqueue.ThesamewiththeAddmethod—justblockwhenthequeueisfullandputanitemassoonasthereisaplaceforaniteminthequeue.Thisapproachhelpsustohandleasituationwhentherearetoomanyproducersortheyjustcreatemoreitemsthatconsumerscanhandle.Thiskindofqueueiscalledaboundedqueue.
AsimpleBoundedBlockingQueueimplementationwilllooklikethis:
publicclassBoundedBlockingQueue<T>
{
privatereadonlyQueue<T>_queue=newQueue<T>();
privatereadonlySemaphoreSlim_nonEmptyQueueSemaphore=
newSemaphoreSlim(0,int.MaxValue);
privatereadonlySemaphoreSlim_nonFullQueueSemaphore;
publicBoundedBlockingQueue(intboundedCapacity)
{
_nonFullQueueSemaphore=newSemaphoreSlim(
boundedCapacity);
}
publicvoidAdd(Tvalue)
{
_nonFullQueueSemaphore.Wait();
lock(_queue)_queue.Enqueue(value);
_nonEmptyQueueSemaphore.Release();
}
publicTTake()
{
_nonEmptyQueueSemaphore.Wait();
Tresult;
www.EBooksWorld.ir
![Page 229: Mastering C# Concurrency - EBooksWorld...a print book customer, you are entitled to a discount on the eBook copy. Get in touch with us at for more details](https://reader035.vdocument.in/reader035/viewer/2022062917/5ed62e6504e9cb4adb670aed/html5/thumbnails/229.jpg)
lock(_queue)
{
Debug.Assert(_queue.Count!=0);
result=_queue.Dequeue();
}
_nonFullQueueSemaphore.Release();
returnresult;
}
}
Thisimplementationusesasimplequeueandtwosemaphores—_nonFullQueueSemaphoreand_nonEmptyQueueSemaphore.Weusethefirstonetoblockproducerswhenthequeueisfull;thesecondblocksconsumerswhenthequeueisempty.WhentheAddmethodiscalled;wecallWaiton_nonFullQueueSemaphore.Itwillreturncontrolwhenthequeueisnotfull,andthenwecanaddanothersemaphorecountertounblockconsumerthreads.TheTakemethodworksexactlylikethis,butinareverseorder—wewaitonthe_nonEmptyQueueSemaphoresemaphoreuntilwehaveanythinginthequeue,andthenweremovetheappearedelementfromthequeueandincreasetheothersemaphorecounter.
TipIntheproductioncode,wewillhavetoimplementIDisposabletosupportdeterministicresourcesreleasing,properexceptionhandling,andcancellationpolicybyprovidingtheCancellationTokeninstancetotheAddandTakemethods.However,inthisexample,itisnotrelevanttothetopicandthislogicisomittedtokeeptheremainingcodecleanandsimple.
Insomecases,theProducer/Consumerqueuecanbeusedtoprocessafixed(oratleastafinite)numberofelements.Inthiscase,weneedtobeabletonotifytheconsumersthatitemsappendingisover:
publicclassBoundedBlockingQueue<T>
{
privatereadonlyQueue<T>_queue=newQueue<T>();
privatereadonlySemaphoreSlim_nonEmptyQueueSemaphore=
newSemaphoreSlim(0,int.MaxValue);
privatereadonly
CancellationTokenSource_consumersCancellationTokenSource=
newCancellationTokenSource();
privatereadonlySemaphoreSlim_nonFullQueueSemaphore;
publicBoundedBlockingQueue(intboundedCapacity)
{
_nonFullQueueSemaphore=newSemaphoreSlim(boundedCapacity);
}
publicvoidCompleteAdding()
{
//Notifyalltheconsumersthatcompletionisfinished
www.EBooksWorld.ir
![Page 230: Mastering C# Concurrency - EBooksWorld...a print book customer, you are entitled to a discount on the eBook copy. Get in touch with us at for more details](https://reader035.vdocument.in/reader035/viewer/2022062917/5ed62e6504e9cb4adb670aed/html5/thumbnails/230.jpg)
_consumersCancellationTokenSource.Cancel();
}
publicvoidAdd(Tvalue)
{
_nonFullQueueSemaphore.Wait();
lock(_queue)_queue.Enqueue(value);
_nonEmptyQueueSemaphore.Release();
}
publicTTake()
{
Titem;
if(!TryTake(outitem))
{
thrownewInvalidOperationException();
}
returnitem;
}
publicIEnumerable<T>Consume()
{
Telement;
while(TryTake(outelement))
{
yieldreturnelement;
}
}
privateboolTryTake(outTresult)
{
result=default(T);
if(!_nonEmptyQueueSemaphore.Wait(0))
{
try
{
_nonEmptyQueueSemaphore.Wait(
_consumersCancellationTokenSource.Token);
}
catch(OperationCanceledExceptione)
{
//Breakingthelooponlywhencancellation
//wasrequestedbyCompleteAdding
if(e.CancellationToken==
_consumersCancellationTokenSource.Token)
{
returnfalse;
}
//Propagateoriginalexception
throw;
}
www.EBooksWorld.ir
![Page 231: Mastering C# Concurrency - EBooksWorld...a print book customer, you are entitled to a discount on the eBook copy. Get in touch with us at for more details](https://reader035.vdocument.in/reader035/viewer/2022062917/5ed62e6504e9cb4adb670aed/html5/thumbnails/231.jpg)
}
lock(_queue)
{
result=_queue.Dequeue();
}
_nonFullQueueSemaphore.Release();
returntrue;
}
}
HereweseenewCompleteAddingandConsumemethods.Thefirstoneisintendedtobeusedfromproducer’scodetosignalthatwehavefinishedappendingitemstothequeue.TheConsumemethodcanbeusedbyconsumerstoprocessalltheitemsuntilthequeueisemptyanditemappendingiscomplete.
WehavealsoimplementedacooperativecancellationherewiththehelpoftheCancellationTokenSourceandCancellationTokenobjects.TheCompleteAddingmethodsetstheflagthatindicatesthatnoadditionalelementswillbeaddedtothecollection.TheTryTakemethodusesthisflagandstandardsemaphorecancellationlogictobreaktheloopwhencancellationisrequested.
Wecanuseourbrandnewcollectioninthefollowingway:
varqueue=newBoundedBlockingQueue<string>(3);
vart1=Task.Run(()=>
{
AddAndPrint(queue,"1");
AddAndPrint(queue,"2");
AddAndPrint(queue,"3");
AddAndPrint(queue,"4");
AddAndPrint(queue,"5");
queue.CompleteAdding();
Console.WriteLine("[{0}]:finishedproducingelements",
Thread.CurrentThread.ManagedThreadId);
});
vart2=Task.Run(()=>
{
foreach(varelementinqueue.Consume())
{
Print(element);
}
Console.WriteLine("[{0}]:Processingfinished.",
Thread.CurrentThread.ManagedThreadId);
});
vart3=Task.Run(()=>
{
foreach(varelementinqueue.Consume())
www.EBooksWorld.ir
![Page 232: Mastering C# Concurrency - EBooksWorld...a print book customer, you are entitled to a discount on the eBook copy. Get in touch with us at for more details](https://reader035.vdocument.in/reader035/viewer/2022062917/5ed62e6504e9cb4adb670aed/html5/thumbnails/232.jpg)
{
Print(element);
}
Console.WriteLine("[{0}]:Processingfinished.",
Thread.CurrentThread.ManagedThreadId);
});
Task.WaitAll(t1,t2,t3);
Inthiscode,weusedoneproducerthreadthatappendsitemstothequeue,andtwoconsumerthreads.Theresultwillbethefollowing:
[4]:Added1
[9]:Took1
[8]:Took2
[4]:Added2
[4]:Added3
[4]:Added4
[4]:Added5
[9]:Took3
[9]:Took5
[4]:finishedproducingelements
[8]:Took4
[9]:Processingfinished.
[8]:Processingfinished.
www.EBooksWorld.ir
![Page 233: Mastering C# Concurrency - EBooksWorld...a print book customer, you are entitled to a discount on the eBook copy. Get in touch with us at for more details](https://reader035.vdocument.in/reader035/viewer/2022062917/5ed62e6504e9cb4adb670aed/html5/thumbnails/233.jpg)
www.EBooksWorld.ir
![Page 234: Mastering C# Concurrency - EBooksWorld...a print book customer, you are entitled to a discount on the eBook copy. Get in touch with us at for more details](https://reader035.vdocument.in/reader035/viewer/2022062917/5ed62e6504e9cb4adb670aed/html5/thumbnails/234.jpg)
TheProducer/Consumerpatternin.NET4.0+Since.NETFramework4.0,therehasbeenastandardBlockingCollection<T>class,soweshouldpreferusingthistocreateourownimplementationssuchasBoundedBlockingQueue<T>.Itcontainsalltherequiredoperationsandallowsustochoosedifferentelementstoragestrategiesusingdifferentconcurrentcollections.
InspiteofBlockingCollection<T>implementingtheICollection<T>interface,itisjustawrapperoveranygeneralconcurrentcollectionthatimplementsIProducerConsumerCollection<T>.TheBlockingpartofthecollectionnamemeansthattheTakemethodblocksuntilnewelementsappearinthecollection.AmoreaccuratenameforthiscollectionwouldbeBoundedBlockingProducerConsumer<T>,sinceitalsoblockstheAddmethodwhenthemaximumunderlyingcollectioncapacityisreached.
Let’suseBlockingCollection<T>tocreateacustomProducer/Consumerimplementationthatallowsustocreateaspecificnumberofconsumerthreads:
publicclassCustomProducerConsumer<T>:IDisposable
{
privatereadonlyAction<T>_consumeItem;
privatereadonlyBlockingCollection<T>_blockingCollection;
privatereadonlyTask[]_workers;
publicCustomProducerConsumer(Action<T>consumeItem,
intdegreeOfParallelism,
intcapacity=1024)
{
_consumeItem=consumeItem;
_blockingCollection=newBlockingCollection<T>(capacity);
_workers=Enumerable.Range(1,degreeOfParallelism)
.Select(_=>Task.Factory.StartNew(Worker,
TaskCreationOptions.LongRunning))
.ToArray();
}
publicvoidProcess(Titem)
{
_blockingCollection.Add(item);
}
publicvoidCompleteProcessing()
{
_blockingCollection.CompleteAdding();
}
publicvoidDispose()
{
//Unblockallworkerseveniftheclient
//didn'tcallCompleteProcessing
www.EBooksWorld.ir
![Page 235: Mastering C# Concurrency - EBooksWorld...a print book customer, you are entitled to a discount on the eBook copy. Get in touch with us at for more details](https://reader035.vdocument.in/reader035/viewer/2022062917/5ed62e6504e9cb4adb670aed/html5/thumbnails/235.jpg)
if(!_blockingCollection.IsAddingCompleted)
{
_blockingCollection.CompleteAdding();
}
Task.WaitAll(_workers);
_blockingCollection.Dispose();
}
privatevoidWorker()
{
foreach(varitemin
_blockingCollection.GetConsumingEnumerable())
{
_consumeItem(item);
}
}
TheconstructorofCustomProducerConsumer<T>acceptsasaparameteranAction<T>delegatethatrepresentstheconsumer,queuesize,andrequiredparallelismdegree.Then,wecreatetherequirednumberofworkerthreadsbycreatingtheTaskobjectswiththeTaskCreationOptions.LongRunningoption.Theprocessmethodisintendedtoappendnewelements,andtheCompleteProcessingmethodsignalsthattherewillbenomoreelementsappendedtothequeue:
Action<string>processor=element=>
{
Console.WriteLine("[{0}]:Processingelement'{1}'",
Thread.CurrentThread.ManagedThreadId,element);
};
varproducerConcumer=newCustomProducerConsumer<string>(
processor,Environment.ProcessorCount);
for(inti=0;i<5;i++)
{
stringitem="Item"+(i+1);
Console.WriteLine("[{0}]:Addingelement'{1}'",
Thread.CurrentThread.ManagedThreadId,item);
producerConcumer.Process("Item"+(i+1));
}
Console.WriteLine("[{0}]:Completeaddingnewelements",
Thread.CurrentThread.ManagedThreadId);
producerConcumer.CompleteProcessing();
//Disposewillblocktillalloperationsgetscompleted
producerConcumer.Dispose();
Ifwerunthiscode,wewillgetthefollowingresult:
[5]:Addingelement'Item1'
[5]:Addingelement'Item2'
www.EBooksWorld.ir
![Page 236: Mastering C# Concurrency - EBooksWorld...a print book customer, you are entitled to a discount on the eBook copy. Get in touch with us at for more details](https://reader035.vdocument.in/reader035/viewer/2022062917/5ed62e6504e9cb4adb670aed/html5/thumbnails/236.jpg)
[5]:Addingelement'Item3'
[9]:Processingelement'Item1'
[8]:Processingelement'Item2'
[5]:Addingelement'Item4'
[5]:Addingelement'Item5'
[5]:Completeaddingnewelements
[9]:Processingelement'Item5'
[10]:Processingelement'Item3'
[11]:Processingelement'Item4'
Theresultshowsthatthereisoneproducerthreadthatappendselementstothecollection,andfourdifferentconsumerthreadsthatprocesstheseelementsuntiltheproducerthreadstopsappendingitems.
www.EBooksWorld.ir
![Page 237: Mastering C# Concurrency - EBooksWorld...a print book customer, you are entitled to a discount on the eBook copy. Get in touch with us at for more details](https://reader035.vdocument.in/reader035/viewer/2022062917/5ed62e6504e9cb4adb670aed/html5/thumbnails/237.jpg)
www.EBooksWorld.ir
![Page 238: Mastering C# Concurrency - EBooksWorld...a print book customer, you are entitled to a discount on the eBook copy. Get in touch with us at for more details](https://reader035.vdocument.in/reader035/viewer/2022062917/5ed62e6504e9cb4adb670aed/html5/thumbnails/238.jpg)
SummaryInthischapter,wehavelearnedaboutdifferentconcurrentdatastructures,theiradvantagesanddisadvantages,andwehaveunderstoodthatchoosinganappropriateconcurrentdatastructureisacomplicatedandresponsibletask.Therightchoiceisdefinedbymanycriteriasuchasavailability,complexity,resourceconsumption,versatility,performance,andmanyothers.
Similartosoftwaredevelopment,ingeneralthereisnosingleandproperuniversalsolutionappropriateforallusagescenarios.Insomecases,itisbettertouseregularcollectionswithexclusivelocking.Someothercaseswillrequiredevelopingourownspecificconcurrentdatastructuresfromscratch,sinceauniversalstandardcollectionwillnotfitinthehighperformancerequirements.Aruleofthumbistotrytoimplementtheeasiestsolutionandthenmeasuretheperformanceandcheckwheretheperformancebottleneckofyourapplicationis.
Inthenextchapter,wewillconsiderdifferentconcurrentandasynchronousprogrammingpatternsthatcanhelpinstructuringyourparallelprogramforsimplicityandefficiencyandallowyoutoquicklyimplementwell-knownconcurrentalgorithms.
www.EBooksWorld.ir
![Page 239: Mastering C# Concurrency - EBooksWorld...a print book customer, you are entitled to a discount on the eBook copy. Get in touch with us at for more details](https://reader035.vdocument.in/reader035/viewer/2022062917/5ed62e6504e9cb4adb670aed/html5/thumbnails/239.jpg)
www.EBooksWorld.ir
![Page 240: Mastering C# Concurrency - EBooksWorld...a print book customer, you are entitled to a discount on the eBook copy. Get in touch with us at for more details](https://reader035.vdocument.in/reader035/viewer/2022062917/5ed62e6504e9cb4adb670aed/html5/thumbnails/240.jpg)
Chapter7.LeveragingParallelPatternsTherearemanyprogrammingrules,tricks,andtypicalpatternsrelatedtoconcurrentprogrammingthathavebeendevelopedtoaddressconcreteproblemsthatoftenhappeninpractice.Inthischapter,wewillgothroughseveralkindsofconcurrentprogrammingpatterns—low-levelpatterns(concurrentidioms),.NET-specificpatternsforasynchronousprogramming(AsynchronousProgrammingPatterns),andhigh-levelconcurrentapplicationbuildingblocks(ConcurrentDesignPatterns).Let’sreviewthemonebyone.
www.EBooksWorld.ir
![Page 241: Mastering C# Concurrency - EBooksWorld...a print book customer, you are entitled to a discount on the eBook copy. Get in touch with us at for more details](https://reader035.vdocument.in/reader035/viewer/2022062917/5ed62e6504e9cb4adb670aed/html5/thumbnails/241.jpg)
ConcurrentidiomsThe.NETFrameworkplatformcontainssomehigh-levelcomponentsthatmakeconcurrentapplicationsprogrammingmucheasier.InChapter6,UsingConcurrentDataStructures,wereviewedconcurrentcollectionsanddatastructures,andinChapter4,TaskParallelLibraryinDepth,andChapter5,C#LanguageSupportforAsynchrony,welookedatTaskParallelLibraryandtheC#languageasync/awaitinfrastructure.
Here,wewillseehowTPLandC#canimproveyourprogrammingexperience.
www.EBooksWorld.ir
![Page 242: Mastering C# Concurrency - EBooksWorld...a print book customer, you are entitled to a discount on the eBook copy. Get in touch with us at for more details](https://reader035.vdocument.in/reader035/viewer/2022062917/5ed62e6504e9cb4adb670aed/html5/thumbnails/242.jpg)
ProcessTasksinCompletionOrderAsanexampletask,let’sconsiderleveragingaweatherinformationfromaserviceforeachprovidedcity,processingtheinformation,andprintingittotheconsole.Thesimpleimplementationwillbelikethis:
publicasyncTaskUpdateWeather()
{
varcities=newList<string>{"LosAngeles","Seattle","NewYork"};
vartasks=
fromcityincities
selectnew{City=city,WeatherTask=GetWeatherForAsync(city)};
foreach(varentryintasks)
{
varweather=awaitentry.WeatherTask;
ProcessWeather(entry.City,weather);
}
}
privateTask<Weather>GetWeatherForAsync(stringcity)
{
Console.WriteLine("Gettingtheweatherfor'{0}'",city);
returnWeatherService.GetWeatherAsync(city);
}
privatevoidProcessWeather(stringcity,Weatherweather)
{
Console.WriteLine("[{2}]:Processingweatherfor'{0}':'{1}'",
city,weather,DateTime.Now.ToLongTimeString());
}
Inthiscode,weusedaLINQquerytogettheweatherdataforeachcity.Theprogramwillworkwell,butthereisaprobleminthiscode;wecalltheweatherinfoserviceonebyoneandanewrequestgetsissuedonlyaftertheprecedingrequesthasbeencompleted.WecanuseaworkaroundbycallingtheToListmethodonthequery,butwewillgettheresultsintheirstartingorderandnotbytaskcompletion.
ThesolutionistousetheProcessTasksinCompletionOrderidiom.TheimplementationisbasedontheTask.WhenAnymethod:
varcities=newList<string>{"LosAngeles","Seattle","NewYork"};
vartasks=cities.Select(asynccity=>
{
returnnew{City=city,Weather=awaitGetWeatherForAsync(city)};
}).ToList();
while(tasks.Count!=0)
{
varcompletedTask=awaitTask.WhenAny(tasks);
tasks.Remove(completedTask);
www.EBooksWorld.ir
![Page 243: Mastering C# Concurrency - EBooksWorld...a print book customer, you are entitled to a discount on the eBook copy. Get in touch with us at for more details](https://reader035.vdocument.in/reader035/viewer/2022062917/5ed62e6504e9cb4adb670aed/html5/thumbnails/243.jpg)
varresult=completedTask.Result;
ProcessWeather(result.City,result.Weather);
}
Here,wecalledtheweatherinformationserviceforallthecitiesinparallel,andtheninsidethewhileloopweusedtheTask.WhenAnymethodtogetthefirstcompletedtask.Thetaskgetsprocessedandremovedfromtherunningtasklist.Asrequiredinthisexample,tasksarebeingprocessedincompletionorder.
However,thecodelooksmorecomplicatedthanthefirstsample.Togetthecodestructured,wecancreateagenericOrderByCompletionimplementationforthetaskscollection:
publicstaticIEnumerable<Task<T>>OrderByCompletion<T>(
thisIEnumerable<Task<T>>taskSequence)
{
vartasks=taskSequence.ToList();
while(tasks.Count!=0)
{
vartcs=newTaskCompletionSource<T>();
//Gettingthefirstfinishedtask
Task.WhenAny(tasks).ContinueWith((Task<Task<T>>tsk)=>{
tasks.Remove(tsk.Result);
tcs.FromTask(tsk.Result);
});
yieldreturntcs.Task;
}
}
NoteNevertheless,thisimplementationhasaseriouspitfall.SincetheTask.WhenAnymethodcreatesacontinuationtaskforeachrunningtaskandwearecallingitinsidetheloop,wecanconcludethatthisOrderByCompletionmethodimplementationhasatimecomplexityofO(n2).Toimprovetheperformance,wecanregisteracontinuationforeachtaskthatwillusetheTaskCompletionSourcearraytostoreeachtask’sresult.
ItisverycomfortabletousethenewlyimplementedOrderByCompletionmethod:
varcities=newList<string>{"LosAngeles","Seattle","NewYork"};
vartasks=cities.Select(asynccity=>
{
returnnew{City=city,Weather=awaitGetWeatherForAsync(city)};
});
foreach(vartaskintasks.OrderByCompletion())
{
vartaskResult=awaittask;
www.EBooksWorld.ir
![Page 244: Mastering C# Concurrency - EBooksWorld...a print book customer, you are entitled to a discount on the eBook copy. Get in touch with us at for more details](https://reader035.vdocument.in/reader035/viewer/2022062917/5ed62e6504e9cb4adb670aed/html5/thumbnails/244.jpg)
//taskResultisanobjectofanonymoustypewithCityand
//WeatherTask
ProcessWeather(taskResult.City,taskResult.Weather);
}
Nowitispossibletousetheplainoldforeachloopsimilarlytothefirstimplementation,butthetaskprocessinghappensbycompletionandnotbystartorder.Theresultswilldemonstratethisprocessingbehavior:
[12:54:35PM]:Gettingtheweatherfor'LosAngeles'
[12:54:35PM]:Gettingtheweatherfor'Seattle'
[12:54:35PM]:Gettingtheweatherfor'NewYork'
[12:54:36PM]:Processingweatherfor'Seattle':'Temp:7C'
Gottheweatherfor'LosAngeles'
[12:54:39PM]:Processingweatherfor'LosAngeles':'Temp:6C'
Gottheweatherfor'NewYork'
[12:54:40PM]:Processingweatherfor'NewYork':'Temp:8C'
www.EBooksWorld.ir
![Page 245: Mastering C# Concurrency - EBooksWorld...a print book customer, you are entitled to a discount on the eBook copy. Get in touch with us at for more details](https://reader035.vdocument.in/reader035/viewer/2022062917/5ed62e6504e9cb4adb670aed/html5/thumbnails/245.jpg)
LimitingtheparallelismdegreeTousecomputerresourceseffectively,weneedtobeabletospecifythenumberofsimultaneouslyrunningoperations.Besidesthis,theoptimalparalleloperationsnumberisrelatedtotheirnature.Iftheseoperationsarelong-runningandCPU-bound,itmakessensetousethenumberofhardware-supportedthreadstolimittheparallelismdegree.
However,iftheyareIO-bound,thereisnoclearlimit.ItdependsonmanyfactorsrelatedtothekindofIOthatishappeningandthecorrespondinghardwarecharacteristics.ItmaybeHDDrandomreadspeed,ornetworkthroughputandlatency,orinthecaseofremoteservicecalls,theperformanceofthisservice,andsoon.Creatingageneralsolutioninthiscaseisveryhardandcanbemorecomplicatedthancreatingourownimplementationofathreadpool,whichdoesthesameforCPU-boundtasks.
However,forstarters,wecanjustrunmultipleparalleloperationsandlimittheparallelismdegreewithacertainnumber.Let’spretendthatwedidexperimentswithourweatherinfoserviceandfoundoutbymeasurementsthatthemosteffectiveoptionistorunonlytwosimultaneousrequeststothisservice.
OneofthewaysofimplementingsuchalimitisbycreatingaForEachAsyncextensionmethodthatacceptadegreeOfParallelismparameter:
publicstaticIEnumerable<Task<TTask>>
ForEachAsync<TItem,TTask>(
thisIEnumerable<TItem>source,
Func<TItem,Task<TTask>>selector,
intdegreeOfParallelism)
{
//Weneedtoknowalltheitemsinthesource
//beforestartingtasks
vartasks=source.ToList();
intcompletedTask=-1;
//CreatinganarrayofTaskCompletionSourcethatwouldhold
//theresultsforeachoperations
vartaskCompletions=newTaskCompletionSource<TTask>[tasks.Count];
for(intn=0;n<taskCompletions.Length;n++)
taskCompletions[n]=newTaskCompletionSource<TTask>();
//Partitionerwoulddoallgruntworkforusandsplit
//thesourceintoappropriatenumberofchunks
//forparallelprocessing
foreach(varpartitioninPartitioner.Create(tasks).
GetPartitions(degreeOfParallelism)){
varp=partition;
//Loosingsynccontextandstartingasynchronous
//computationforeachpartition
Task.Run(async()=>
{
while(p.MoveNext())
www.EBooksWorld.ir
![Page 246: Mastering C# Concurrency - EBooksWorld...a print book customer, you are entitled to a discount on the eBook copy. Get in touch with us at for more details](https://reader035.vdocument.in/reader035/viewer/2022062917/5ed62e6504e9cb4adb670aed/html5/thumbnails/246.jpg)
{
vartask=selector(p.Current);
//Don'twanttouseemptycatch.
//Thistrickjustswallowsanexception
awaittask.ContinueWith(_=>{});
intfinishedTaskIndex=Interlocked.Increment(
refcompletedTask);
taskCompletions[finishedTaskIndex]
.FromTask(task);
}
});
}
returntaskCompletions.Select(tcs=>tcs.Task);
}
Thereareseveraloptionsthatwecanchoosetoimplementalimitonthedegreeofparallelism.Forexample,wecanusesemaphoresorothersynchronizationprimitives.However,wecanchoosemorecomfortableoptionstouseTaskParallelLibraryanditsPartitionertypetogetasetofpartitionswiththePartitioner.CreatePartitionermethodcall.Eachofthesepartitionsrepresentssomethinglikeaniteratorthatcanbeusedinparallelwithotherpartitions.Tostorethecompletedtasks,wewilluseanarrayofTaskCompletionSourceobjects,whichwillholdtheresultsincompletionorder.
Thewayofusingthismethodisshowninthefollowingexample:
varcities=newList<string>{"LosAngeles","Seattle","NewYork","San
Francisco"};
vartasks=cities.ForEachAsync(asynccity=>
{
returnnew{City=city,Weather=awaitGetWeatherForAsync(city)};
},2);
foreach(vartaskintasks)
{
vartaskResult=awaittask;
ProcessWeather(taskResult.City,taskResult.Weather);
}
Thesearetheresults:
[1:22:09PM]:Gettingtheweatherfor'LosAngeles'
[1:22:09PM]:Gettingtheweatherfor'Seattle'
Heretheparallelismlimitstartedtowork.Wewillnotrunmoretasksuntiloneofthemiscompleted:
[1:22:10PM]:Processingweatherfor'LosAngeles':'Temp:6C'
Thefirsttaskhasfinished;nowwecanrunonemoretask:
www.EBooksWorld.ir
![Page 247: Mastering C# Concurrency - EBooksWorld...a print book customer, you are entitled to a discount on the eBook copy. Get in touch with us at for more details](https://reader035.vdocument.in/reader035/viewer/2022062917/5ed62e6504e9cb4adb670aed/html5/thumbnails/247.jpg)
[1:22:10PM]:Gettingtheweatherfor'NewYork'
Thistaskiscompletedatonce:
[1:22:15PM]:Processingweatherfor'NewYork':'Temp:8C'
Here,werunonemoretask:
[1:22:15PM]:Gettingtheweatherfor'SanFrancisco'
Nowthesecondtaskiscompleted:
[1:22:16PM]:Processingweatherfor'Seattle':'Temp:7C'
Heregoesthelasttask:
[1:22:20PM]:Processingweatherfor'SanFrancisco':'Temp:4C'
Thisistheillustrationofthepreviousprocess:
Herewehavetwopartitions;eachoftheserunsasetoftasks.Thesecondpartitionisabletorunonlyonetask,becauseitrunsforalongtime.Thefirstpartitionmanagedtorunthreetasks.Thenumberofpartitionslimitsthedegreeofparallelism.
www.EBooksWorld.ir
![Page 248: Mastering C# Concurrency - EBooksWorld...a print book customer, you are entitled to a discount on the eBook copy. Get in touch with us at for more details](https://reader035.vdocument.in/reader035/viewer/2022062917/5ed62e6504e9cb4adb670aed/html5/thumbnails/248.jpg)
SettingatasktimeoutOperationcancellationsupportisbuiltintotheTaskParallelLibrary;many.NETFrameworkclasses,aswellasthird-partycode,supportitandallowustoprovideacancellationmechanismincaseanoperationtimeouthappens.Someoftheseclassesmakeprogrammingeasierandallowyoutoprovidejustatimeoutvaluefortheoperation.
However,notallcodehasthispotential.Besidesthis,weoftenoperatewithataskthathasbeenalreadystartedandwecannotconfigurethetimeoutvalueintheoperation.Itisaverycommonproblemandthereisasolutionforthis:
publicstaticasyncTask<T>WithTimeout<T>(thisTask<T>task,
TimeSpantimeout)
{
//Covertwocornercases:whentaskiscompletedandwhen
//timeoutisinfinite
if(task.IsCompleted||timeout==Timeout.InfiniteTimeSpan)
{
returnawaittask;
}
varcts=newCancellationTokenSource();
if(awaitTask.WhenAny(task,Task.Delay(timeout,cts.Token))==task)
{
cts.Cancel();
returnawaittask;
}
//Observepotentialexceptionfromtheoriginaltask
task.ContinueWith(_=>{},
TaskContinuationOptions.ExecuteSynchronously);
thrownewTimeoutException();
}
NowwecanusetheWithTimeoutmethodonanytasktosetthetimeoutvaluefortheoperation.Wecanusethismethodlikethis:
try
{
Weatherweather=await
WeatherService.GetWeatherAsync("NewYork").
WithTimeout(TimeSpan.FromSeconds(2));
ProcessWeather(weather);
}
catch(TimeoutException)
{
Console.WriteLine("Taskwastimedout!");
}
Theimplementationlookssimple,butthereareacoupleofimportantnuances:
Inthebeginning,wecheckforsituationswherethetaskhasbeencompletedalready,
www.EBooksWorld.ir
![Page 249: Mastering C# Concurrency - EBooksWorld...a print book customer, you are entitled to a discount on the eBook copy. Get in touch with us at for more details](https://reader035.vdocument.in/reader035/viewer/2022062917/5ed62e6504e9cb4adb670aed/html5/thumbnails/249.jpg)
orwehaveaninfinitetimeoutvalue.Inthiscase,itisenoughtousetheC#awaitstatementtogetthetaskresultinasafemanner.Ifthetaskcompletesbeforethetimeout,wecancelthecorrespondingTask.Delaytimertask.Thislookslikeaslightoptimization,butitcanhaveanoticeableimpactontheapplicationperformance.Wetrytoobservetheprovidedtaskexception,whichisaveryimportantthingtodo.Ifwedonotdoso,wecouldeasilycauseTaskScheduler.TaskUnobservedExceptiontoberaised.In.NET4.5+,itwillnotruinyourapplicationatonce,butitshouldbeavoidedanyway.
www.EBooksWorld.ir
![Page 250: Mastering C# Concurrency - EBooksWorld...a print book customer, you are entitled to a discount on the eBook copy. Get in touch with us at for more details](https://reader035.vdocument.in/reader035/viewer/2022062917/5ed62e6504e9cb4adb670aed/html5/thumbnails/250.jpg)
www.EBooksWorld.ir
![Page 251: Mastering C# Concurrency - EBooksWorld...a print book customer, you are entitled to a discount on the eBook copy. Get in touch with us at for more details](https://reader035.vdocument.in/reader035/viewer/2022062917/5ed62e6504e9cb4adb670aed/html5/thumbnails/251.jpg)
AsynchronouspatternsSincereleasingthefirstversionofC#andthe.NETFramework,therehasbeenbuilt-insupportforrunningasynchronousoperations.Unfortunately,thisinfrastructurewasquitecomplicatedandhardtouse,andthiscausedthenextplatformversionstoincludenewways(patterns)ofwritingasynchronouscodethatenhancedasynchronousprogrammingexperience.
Here,wewillreviewthreeasynchronousprogrammingpatternsstartingfromtheoldest:
APM:AsynchronousProgrammingModel(introducedinthe.NETFramework1.0)EAP:Event-BasedAsynchronousPattern(releasedwiththe.NETFramework2.0)TAP:Task-BasedAsynchronousPattern(appearedwiththe.NETFramework4.0)
ThefirsttwopatternsareusuallyconsideredaslegacycodeandshouldbeusedonlyinsupportscenarioswherethereisnopossibilitytousethetaskinfrastructurefromTaskParallelLibrary.
www.EBooksWorld.ir
![Page 252: Mastering C# Concurrency - EBooksWorld...a print book customer, you are entitled to a discount on the eBook copy. Get in touch with us at for more details](https://reader035.vdocument.in/reader035/viewer/2022062917/5ed62e6504e9cb4adb670aed/html5/thumbnails/252.jpg)
AsynchronousProgrammingModelTheAsynchronousProgrammingModel(APM)structureisasfollows:
//Synchronousoperation
publicResultOperation(intinput,refintinOut,outintoutput);
//Firstmethodthatdenotesbeginningoftheasynchronous
//operation
publicIAsyncResultBeginOperation(intinput,refintinOut,outint
output,AsyncCallbackcallback,objectstate);
//Secondmethodthatshouldbecalledwhentheoperationis
//completed
publicResultEndOperation(refintinOut,outintoutput,IAsyncResult
asyncResult);
Thispatternisstructuredinthefollowingway:anasynchronousoperationsplitsintotwomethods—BeginOperationName/EndOperationName,wheretheOperationNamepartisanactualnameofthisoperation.TheBeginOperationNamemethodacceptsinputparameters,startsanasynchronousoperation,andreturnssomekindofoperationstatethatisrepresentedbyanobjectimplementingIAsyncResultinterface.Usually,italsoacceptsanadditionaloperationcontext—thestateparameter,andacallbackthatwillbecalledwhentheoperationcompletes.
Togettheoperationresultandoperationexceptionhandling,weneedtocalltheEndOperationNamemethod.Iftheoperationisalreadycomplete,thismethodwillimmediatelyreturntheresultorthrowanexception.Iftheoperationisstillrunning,thismethodcallwillbeblockeduntiltheoperationcompletes.
IAsyncResultprovidesaWaitHandleinstancethatcanbeusedtodeterminewhethertheoperationhasbeencompleted,orwhethertheoperationhascompletedsynchronously.
AsanexampleofusingtheAPMpattern,let’simplementtheweatherinformationservicecallandexplainthecodestepbystep:
publicclassWeatherService
{
privatereadonlyFunc<string,Weather>_getWeatherFunc;
publicWeatherService()
{
_getWeatherFunc=GetWeather;
}
publicWeatherGetWeather(stringcity)
{
//Originalsynchronousimplementation
}
publicIAsyncResultBeginGetWeather(stringcity,AsyncCallbackcallback,
objectstate){
return_getWeatherFunc.BeginInvoke(city,callback,state);
}
publicWeatherEndGetWeather(IAsyncResultasyncResult)
www.EBooksWorld.ir
![Page 253: Mastering C# Concurrency - EBooksWorld...a print book customer, you are entitled to a discount on the eBook copy. Get in touch with us at for more details](https://reader035.vdocument.in/reader035/viewer/2022062917/5ed62e6504e9cb4adb670aed/html5/thumbnails/253.jpg)
{
return_getWeatherFunc.EndInvoke(asyncResult);
}
}
Here,inthisexample,wesimulatedanasynchronousoperationwithanasynchronousdelegateinvocation.TherealAPMimplementationincludingremoteservicecalldetailsistoocomplicated,anditdoesnotmakesensetoillustratetheAPMpattern.
Then,wewillwriteaclientwithAPM:
varweatherServce=newWeatherService();
//Pseudoasynchronouscall
stringnewYork="NewYork";
IAsyncResultar1=weatherServce.BeginGetWeather(newYork,callback:null,
state:null);
ar1.AsyncWaitHandle.WaitOne();
Weatherweather1=weatherServce.EndGetWeather(ar1);
ProcessWeather(newYork,weather1);
//Realasynchronousversion
stringseattle="Seattle";
weatherServce.BeginGetWeather(seattle,callback:(IAsyncResultasyncResult)
=>
{
varcontext=(Tuple<string,WeatherService>)asyncResult.AsyncState;
try
{
Weatherweather=context.Item2.EndGetWeather(asyncResult);
ProcessWeather(context.Item1,weather);
}
catch(Exceptione)
{
HandleWeatherError(e);
}
},
state:Tuple.Create(seattle,weatherServce));
ThefirstpieceofcodeshowshowwecancallanasynchronousoperationintheAPMparadigm.WestartedwiththeBeginGetWeathermethodcall,thenimmediatelycalledar1.WaitHandle.WaitOne.WecansimplycallweatherService.EndGetWeatherinsteadandgetthesameresult.
Thenweusedarealasynchronousoperationcall.WehaveusedboththelastinputparametersoftheBeginGetWeathermethod—callbackandstate.Noticethatthereisnocontextcapture—thecontextgetsintoasynchronousoperationthroughastateparameter.
TheAPMpatternhasthefollowingfeatures:
Low-levelpattern:Thiswasintroducedinthefirst.NETFrameworkversionandisusedformanyasynchronousoperationsintheBaseClassLibrary.Lowperformanceoverhead:Thecallbackmethodiscalledonthesamethreadwheretheasynchronousoperationcompleted.Noadditionaloperationsfor
www.EBooksWorld.ir
![Page 254: Mastering C# Concurrency - EBooksWorld...a print book customer, you are entitled to a discount on the eBook copy. Get in touch with us at for more details](https://reader035.vdocument.in/reader035/viewer/2022062917/5ed62e6504e9cb4adb670aed/html5/thumbnails/254.jpg)
synchronizationcontextcapturingoccurs.
Besides,itisveryhardtocombineseveralasynchronousoperations,soonedependsonanother.
Couplingbetweentheasynchronousoperationprovideranditsconsumers:Asynchronousoperationisnotafirst-classobject.Itisnotpossibletoinitiatetheoperation,thentopassitsomehowtotheothercodeandhandleitthere.Aclassthatprovidesanasynchronousoperationanditsclientclasseshaveatightconnection.This,aswell,makesunittestingforsuchoperationsveryhard,sinceitisveryhardtocreateamockasynchronousoperation.
TheAPMcanbeusedinthefollowingscenario—onlyforlegacycodesupport.Task-basedasynchronouspatternscandoeverythingAPMcando.Alsotheyhavealowperformanceoverhead,butaremodernandeasytouse—especiallywiththeC#async/awaitstatements.
www.EBooksWorld.ir
![Page 255: Mastering C# Concurrency - EBooksWorld...a print book customer, you are entitled to a discount on the eBook copy. Get in touch with us at for more details](https://reader035.vdocument.in/reader035/viewer/2022062917/5ed62e6504e9cb4adb670aed/html5/thumbnails/255.jpg)
Event-basedAsynchronousPatternAnEvent-basedAsynchronousPattern(EAP)structureisasfollows:
//Synchronousoperation
publicResultOperation(intinput,refintinOut,outintoutput);
//RaisedwhentheOperationfinished(successfuly,withexceptionorwas
cannelled)
publiceventEventHandler<OperationCompletedEventArgs>OperationCompleted;
//Reportexecutionprogress
publiceventEventHandler<ProgressChangedEventArgs>
OperationProgressChanged;
//Methodthatstartsasynchronousexecution
publicvoidOperationAsync(intinput,refintinOut);
//Methodthatstartsasynchronousexecutionandgetsadditionaluser
definedstate
publicvoidOperationAsync(intinput,refintinOut,objectuserState);
//Cancelpendingoperation
publicvoidCancelAsync(objectstate);
publicclassOperationCompletedEventArgs:AsyncCompletedEventArgs
{
publicOperationCompletedEventArgs(
Exceptionerror,boolcancelled,objectuserState)
:base(error,cancelled,userState)
{
}
publicResultResult{get;internalset;}
publicintInOut{get;internalset;}
publicintOutput{get;internalset;}
}
EAPwasimplementedin.NETFramework2.0andwasdesignedtobeusedinapplicationUIcomponents.Mostofthe.NETtypesthatimplementthispatterninherittheSystem.ComponentModel.ComponentclassaswellandcanbeeasilyusedwithWindowsFormsorWPFdesign-timeeditor.
ThemainideabehindEAPistouseeventsfornotificationaboutasynchronousoperationcompletion.WestarttheoperationwiththeOperationNameAsyncmethod,andthecompletioneventnameisusuallyOperationNameCompleted.Besidesthis,thereareotherevents,forexampletheOperationProgressChangedeventthatallowsustotracktheoperation’sexecutionprogress.
Animportantfeatureofthispatternisthattheseeventsusethesamesynchronizationcontextwheretheasynchronousoperationhasbeenstarted.IfweusetheUIthreadtorunthisoperation,thenitispossibletouseUIcontrolsfromtheeventhandlersmethodofthecomponentthatimplementsEAP,whichmakesthecodecleanandcomfortabletowrite.
Let’simplementaweatherinformationservicewithEAP:www.EBooksWorld.ir
![Page 256: Mastering C# Concurrency - EBooksWorld...a print book customer, you are entitled to a discount on the eBook copy. Get in touch with us at for more details](https://reader035.vdocument.in/reader035/viewer/2022062917/5ed62e6504e9cb4adb670aed/html5/thumbnails/256.jpg)
publicclassWeatherService
{
privatebool_isOperationRunning=false;
privatereadonlySendOrPostCallback_operationFinished;
publicWeatherService()
{
//Thisdelegateshouldbecalled
//incapturedsynccontext
_operationFinished=ProcessOperationFinished;
}
publicWeatherGetWeather(stringcity)
{
//Originalsynchronousimplementation
}
publiceventEventHandler<GetWeatherCompletedEventArgs>
GetWeatherCompleted;
publicvoidGetWeatherAsync(stringcity,objectuserState)
{
if(_isOperationRunning)
thrownewInvalidOperationException();
_isOperationRunning=true;
AsyncOperationoperation=AsyncOperationManager
.CreateOperation(userState);
//RunningGetWeatherasynchronously
ThreadPool.QueueUserWorkItem(state=>
{
GetWeatherCompletedEventArgsargs=null;
try
{
varweather=GetWeather(city);
args=newGetWeatherCompletedEventArgs(weather,state);
}
catch(Exceptione)
{
args=newGetWeatherCompletedEventArgs(e,state);
}
//UsingAsyncOperationthatwillmarshalcontrol
//flowtothesynchronizationcontextthatwas
//capturedatthebeginningofthismethod.
operation.PostOperationCompleted(_operationFinished,args);
},userState);
}
privatevoidProcessOperationFinished(objectstate)
{
//Markthatcurrentoperationiscompleted
www.EBooksWorld.ir
![Page 257: Mastering C# Concurrency - EBooksWorld...a print book customer, you are entitled to a discount on the eBook copy. Get in touch with us at for more details](https://reader035.vdocument.in/reader035/viewer/2022062917/5ed62e6504e9cb4adb670aed/html5/thumbnails/257.jpg)
_isOperationRunning=false;
varargs=(GetWeatherCompletedEventArgs)state;
varhandler=GetWeatherCompleted;
if(handler!=null)
handler(this,args);
}
}
TheGetWeatherAsyncmethodcontainsthemainpatternlogic.First,wecreatedtheAsyncOperationobject,wherewecapturedthecurrentsynchronizationcontextwithSynchronizationContext.Current.Then,weusedtheThreadPool.QueueUserWorkItemmethodtoruntheGetWeatheroperationasynchronouslyonathreadpool.Thenweusedoperation.PostOperationCompletedtopostanotificationaboutoperationcompletiononthecapturedsynchronizationcontext.ThiswillalloweventsubscriberstohandletheGetWeatherCompletedeventsafelyandwillmakeitpossibletouseUIcontrolswithoutusingtheControl.InvokeandDispatcher.BeginInvokemechanics.
Nowlet’slookathowtousetheservicewithEAP:
varweatherService=newWeatherService();
varcity="NewYork";
//Startasynchronousoperation
weatherService.GetWeatherAsync(city,userState:null);
//IfcurrentmethodisrunninginUIthread
//followingeventhandlerwouldbeexecutedintheUIthread
weatherService.GetWeatherCompleted+=(sender,args)=>{
Weatherresult=args.Result;
ProcessWeather(city,result);
};
TheEAPfeaturesareasfollows:
High-levelpattern:EAPallowsustoconsumeasynchronousoperationswitheaseaswellasstartnewonesHighoverhead:Sinceoperationcompletioneventsalwaysgetpostedtothecapturedsynchronizationcontext,thispatternisnotintendedtobeusedfromlow-levelcomponentsthatdointensiveIOoperationsIntendedforUIcomponents:SinceEAPwasdesignedforaveryspecificscenario(UIcomponents),itmightnotbethebestchoicetoprogramsomeotherfeaturesComplicatedimplementation:WhilethisisdefinitelyeasierthanAPM,itisstillhardtoprogramreal-worldscenarioswithoperationprogressandcancellationCouplingbetweenasynchronousoperationprovideranditsconsumers:Similartothepreviouspattern,thisonealsocreatestightcouplingbetweentheoperationclassandclientclasses
EAPcanusedinthefollowingscenario—legacycodesupport.Nevertheless,ifyouwriteanewcodeyoushouldnotuseEAP.Task-basedasyncpatternhaseverythingthatEAPhas,butitalsohaslanguagelevelsupport,loosecoupling,andalotofotherusefulfeatures.
www.EBooksWorld.ir
![Page 258: Mastering C# Concurrency - EBooksWorld...a print book customer, you are entitled to a discount on the eBook copy. Get in touch with us at for more details](https://reader035.vdocument.in/reader035/viewer/2022062917/5ed62e6504e9cb4adb670aed/html5/thumbnails/258.jpg)
Task-basedAsynchronousPatternTaskParallelLibraryhasbeenexistingsincethe.NETFramework4.0release,anditintroducedanewasynchronousprogrammingpattern—Task-basedAsynchronousPattern(TAP).Thispatternconsistsofthefollowingmethods:
//Synchronousoperation
publicResultOperation(intinput,refintinOut,outintoutput);
//Asynchronousversion
publicTask<WrappedResult>OperationAsync(intinput,intinOut);
//Customresultthatwrapsin/outandoutparameters
publicclassWrappedResult
{
publicResultResult{get;internalset;}
publicintInOut{get;internalset;}
publicintOutput{get;internalset;}
}
SimilartoEAP,TAPusesthesamenamingscheme.OperationsarenamedOperationNameAsyncbyaddingtheAsyncsuffixtothesynchronousimplementationname.However,themainideabehindTAPistouseaspecialTaskobjectthatrepresentsanasynchronousoperationwithoutanyreturnvalue,andTask<T>forthosethatreturnresultsoftheTtype.SincewecanaccesstheresultonlythroughtheTask.Resultproperty,everyinputandoutputparametermustbeapartofthereturnvalue.
Thefollowingisonemoreweatherinformationserviceimplementation:
publicclassWeatherService
{
publicWeatherGetWeather(stringcity)
{
//Originalsynchronousimplementation
}
publicTask<Weather>GetWeatherAsync(stringcity)
{
returnTask.Run(()=>GetWeather(city));
}
}
TheeasiestimplementationthatseemstobeobviousistowrapasynchronousmethodintoataskwiththehelpoftheTask.Runmethod.However,thisapproachshouldnotbeusedinreal-worldapplications,unlessyouarecompletelysureaboutwhatisgoingon.
NoteThisantipatterniscalled“asyncoversync”andusingthiswillleadtoscalabilityandperformanceproblemsinyourapplication.Mostofthetrulyasynchronousoperationsinthe.NETFrameworkareIO-bound,andthusdonotrequireusingadditionalthreads.ThistopicwillbereviewedindetailinChapter8,Server-SideAsynchrony.
Let’slookatthefollowingcode:
publicasyncTaskProcessWeatherFromWeatherService()
www.EBooksWorld.ir
![Page 259: Mastering C# Concurrency - EBooksWorld...a print book customer, you are entitled to a discount on the eBook copy. Get in touch with us at for more details](https://reader035.vdocument.in/reader035/viewer/2022062917/5ed62e6504e9cb4adb670aed/html5/thumbnails/259.jpg)
{
varweatherService=newWeatherService();
stringcity="SanFrancisco";
//IfthismethodwascalledintheUIthread,
//"awaiter"willcapturesynchronizationcontext
//andProcessWeathermethodwouldbecalledintheUIthreadas
//well
Weatherweather=awaitweatherService.GetWeatherAsync(city);
ProcessWeather(city,weather);
}
Task-basedAsynchronousPatternisnowthemostpopularandmostconvenientwaytodevelopasynchronousapplications.Itcanbecharacterizedbythefollowing:
Lowoverhead:Taskshavelowoverheadandcanbeusedinhigh-loadscenarios.High-level:TaskisahighlevelabstractionthatprovidesaconvenientAPItocombineasynchronousoperations,tocaptureornotcapturethecurrentsynchronizationcontextifneeded,convertolderAPMandEAPpatternstoTAP,andmanyotherfeatures.Comfortabletouse:Thispatterniseasytousebydevelopers,butatthesametime,ithasarichAPIandmorefeaturesthantheprevioustwo.LanguagesupportinC#/VB:C#andVB.NEThasbuilt-inasync/awaitstatementsthatmakeasynchronousprogrammingmucheasier.ThisinfrastructureisbasedontheTaskandTask<T>types.
NoteAswesawinChapter5,C#LanguageSupportforAsynchrony,awaitcanbeusedwithanytypethathasitsownmethodoranextensionmethodcalledGetAwaiterwithoutparameters,whichreturnstheobjectthatimplementstheINotifyCompletioninterfaceandcontainstheIsCompletedBooleanpropertyandtheGetResultmethodwithnoparameters.
TaskandTask<T>arefirst-classobjects:Unlikepreviouspatterns,ataskinstanceisself-sufficient.Itcanbepassedasaparametertoothermethodsorcanbestoredinavariableorinstancefield.Ifyouhaveaccesstothetaskinstance,youwillhavefullcontroloverthecorrespondingasynchronousoperation.Wedonotneedtousetheasynchronousoperationclassasecondtimetofinishtheoperation.Wecantestsuchoperationsandreturnanalreadycompletedtaskfromamockmethod.Gettingridofsideeffects:UsingtheTask<T>classencouragestheavoidanceofsideeffectsintheprogram,whichisveryimportanttoreducecontentionandimprovescalability.
www.EBooksWorld.ir
![Page 260: Mastering C# Concurrency - EBooksWorld...a print book customer, you are entitled to a discount on the eBook copy. Get in touch with us at for more details](https://reader035.vdocument.in/reader035/viewer/2022062917/5ed62e6504e9cb4adb670aed/html5/thumbnails/260.jpg)
www.EBooksWorld.ir
![Page 261: Mastering C# Concurrency - EBooksWorld...a print book customer, you are entitled to a discount on the eBook copy. Get in touch with us at for more details](https://reader035.vdocument.in/reader035/viewer/2022062917/5ed62e6504e9cb4adb670aed/html5/thumbnails/261.jpg)
ConcurrentpatternsWehavealreadyreviewedsomeofthesepatternsearlierinthisbook.Forexample,inChapter4,TaskParallelLibraryinDepth,westudiedParallel.InvokeandParallel.Foreach,whichactuallyisanimplementationofthefork/joinpattern.InChapter6,UsingConcurrentDataStructures,wereviewedaProducer/Consumerpatternimplementation.However,thereisaveryimportantscenariothatwehavenotseenyet.Itiscalledaparallelpipeline.
www.EBooksWorld.ir
![Page 262: Mastering C# Concurrency - EBooksWorld...a print book customer, you are entitled to a discount on the eBook copy. Get in touch with us at for more details](https://reader035.vdocument.in/reader035/viewer/2022062917/5ed62e6504e9cb4adb670aed/html5/thumbnails/262.jpg)
ParallelpipelinesUsuallyacomplexparallelcomputationcanbeconsideredasseveralstagescombinedintosomesortofapipeline.Thelatterstageneedstheresultsoftheformer,andthispreventsthesestagesfromrunninginparallel.However,thecalculationsinsideeachstagecanbeindependent,whichallowsustoparallelizeeachstageitself.Besidesthis,wecansimultaneouslyrunallthestages,assumingthatwecanprocessstageresultsonebyone,sowedonothavetowaituntileachstagecomputesalltheresultsbeforeproceedingtothenextstage.Insteadofthis,wegetanitemfromapreviousstageassoonasitisreadyandpassitalongtothenextstage,andsoonandsoforth,untilthefinalstage.Thiswayoforganizingparallelcomputationsisknownasparallelpipeline,whichisaspecialcaseofaProducer/Consumerpattern.Itallowsustoachievealmostparallelprocessingofstagecomputations,shiftedbythetimethatisrequiredtogetthefirststageresult.
ThefollowingcodeshowshowtoimplementaparallelpipelineusingastandardBlockingCollectiondatastructure:
privateconstintParallelismDegree=4;
privateconstintCount=1;
staticvoidMain(string[]args){
varcts=newCancellationTokenSource();
Task.Run(()=>{
if(Console.ReadKey().KeyChar=='c'){
cts.Cancel();
}
});
varsourceArrays=newBlockingCollection<string>[ParallelismDegree];
for(inti=0;i<sourceArrays.Length;i++){
sourceArrays[i]=newBlockingCollection<string>(Count);
}
vargetWeatherStep=newPipelineWorkerAsync<string,Weather>(
sourceArrays,
city=>WeatherService.GetWeatherAsync(city),
cts.Token,
"GetWeather",
Count
);
varconvertTempStep=newPipelineWorkerAsync<Weather,Tuple<string,
decimal>>(
getWeatherStep.Output,
weather=>Task.FromResult(Tuple.Create(weather.City,
weather.TemperatureCelcius*(decimal)9/5+32)),
cts.Token,
"ConvertTemperature",
Count
);
varprintInfoStep=newPipelineWorkerAsync<Tuple<string,decimal>,
string>(
www.EBooksWorld.ir
![Page 263: Mastering C# Concurrency - EBooksWorld...a print book customer, you are entitled to a discount on the eBook copy. Get in touch with us at for more details](https://reader035.vdocument.in/reader035/viewer/2022062917/5ed62e6504e9cb4adb670aed/html5/thumbnails/263.jpg)
convertTempStep.Output,
t=>Console.WriteLine("Thetemperaturein{0}is{1}Fonthreadid
{2}",t.Item1,t.Item2,Thread.CurrentThread.ManagedThreadId),
cts.Token,
"PrintInformation"
);
try{
Parallel.Invoke(
()=>{
Parallel.ForEach(
new[]{"Seattle","NewYork","LosAngeles","SanFrancisco"},
(city,state)=>{
if(cts.Token.IsCancellationRequested){
state.Stop();
}
AddCityToSourceCollection(sourceArrays,city,cts.Token);
});
foreach(vararrinsourceArrays){
arr.CompleteAdding();
}
},
()=>getWeatherStep.RunAsync().GetAwaiter().GetResult(),
()=>convertTempStep.RunAsync().GetAwaiter().GetResult(),
()=>printInfoStep.RunAsync().GetAwaiter().GetResult()
);
}
catch(AggregateExceptionae){
foreach(varexinae.InnerExceptions)
Console.WriteLine(ex.Message+ex.StackTrace);
}
if(cts.Token.IsCancellationRequested){
Console.WriteLine("Operationhasbeencanceled!PressENTERtoexit.");
}
else{
Console.WriteLine("PressENTERtoexit.");
}
Console.ReadLine();
}
staticvoidAddCityToSourceCollection
BlockingCollection<string>[]cities,stringcity,
CancellationTokentoken){
BlockingCollection<string>.TryAddToAny(cities,city,50,token);
Console.WriteLine("Added{0}tofetchweatheronthreadid{1}",city,
Thread.CurrentThread.ManagedThreadId);
Thread.Sleep(TimeSpan.FromMilliseconds(100));
}
Atthebeginningoftheprecedingcode,weareimplementingacancellationoperationforthepipelinebyrunningaseparatetaskthatislisteningfortheCkeypress.WhentheuserpressestheCbutton,thetaskrunscts.Cancelthatsignalsacancellationoperationtothesharedcancellationtoken.Thistokengoesintoallthefurtheroperationsandisableto
www.EBooksWorld.ir
![Page 264: Mastering C# Concurrency - EBooksWorld...a print book customer, you are entitled to a discount on the eBook copy. Get in touch with us at for more details](https://reader035.vdocument.in/reader035/viewer/2022062917/5ed62e6504e9cb4adb670aed/html5/thumbnails/264.jpg)
canceltheentireparallelpipelineatonce.
Now,wedefinethepipelinebehavior.First,wesettheparallelismdegreeforourparallelpipeline.Inthefollowingexample,wewillcreatefourblockingcollectionsforoneelementeach.Itwillcausefourelementstobeprocessedinparallel.Ifweneedtochangethis,wecanusetwocollectionsfortwoelements,andsoon.
Next,wewilldefinepipelinesteps.Thefirststepisresponsibleforgettingweatherinformationforeachcitythatappearsinthesourcecollection.ThenthenextstepwillconvertthetemperaturefromCelsiustoFahrenheit.Thefinalstepwillprintouttheweatherinformationtotheconsole.
Allweneedtodonowisruntheentirepipeline.WewillusetheParallel.Invokestatementtorunallthepipelinestagesinparallel,andinthefirststage,wewilluseParallel.Foreachtofillinthecitiescollectioninparallelaswell:
classPipelineWorkerAsync<TInput,TOutput>
{
Func<TInput,Task<TOutput>>_processorAsync=null;
Action<TInput>_outputProcessor=null;
BlockingCollection<TInput>[]_input;
CancellationToken_token;
privateint_count;
publicPipelineWorkerAsync(
BlockingCollection<TInput>[]input,
Func<TInput,Task<TOutput>>processorAsync,
CancellationTokentoken,
stringname,
intcount)
{
_input=input;
_count=count;
_processorAsync=processorAsync;
_token=token;
Output=newBlockingCollection<TOutput>[_input.Length];
for(inti=0;i<Output.Length;i++)
Output[i]=null==input[i]?null:newBlockingCollection
<TOutput>(Count);
Name=name;
}
publicPipelineWorkerAsync(
BlockingCollection<TInput>[]input,
Action<TInput>renderer,
CancellationTokentoken,
stringname){
_input=input;
_outputProcessor=renderer;
_token=token;
Name=name;
Output=null;
}
www.EBooksWorld.ir
![Page 265: Mastering C# Concurrency - EBooksWorld...a print book customer, you are entitled to a discount on the eBook copy. Get in touch with us at for more details](https://reader035.vdocument.in/reader035/viewer/2022062917/5ed62e6504e9cb4adb670aed/html5/thumbnails/265.jpg)
publicBlockingCollection<TOutput>[]
Output{get;privateset;}
publicstringName{get;privateset;}
publicasyncTaskRunAsync(){
Console.WriteLine("{0}isrunning",this.Name);
List<Task>tasks=newList<Task>();
foreach(varbcin_input){
varlocal=bc;
vart=Task.Run(newFunc<Task>(async()=>{
TInputreceivedItem;
while(!local.IsCompleted&&!_token.IsCancellationRequested){
varok=local.TryTake(outreceivedItem,50,_token);
if(ok){
if(Output!=null){
TOutputoutputItem=await_processorAsync(receivedItem);
BlockingCollection<TOutput>.AddToAny(Output,outputItem);
Console.WriteLine("{0}sent{1}tonext,onthreadid
{2}",Name,outputItem,Thread.CurrentThread.ManagedThreadId);
Thread.Sleep(TimeSpan.FromMilliseconds(100));
}
else{
_outputProcessor(receivedItem);
}
}
else{
Thread.Sleep(TimeSpan.FromMilliseconds(50));
}
}
}),
_token);
tasks.Add(t);
}
awaitTask.WhenAll(tasks);
if(Output!=null){
foreach(varbcinOutput)bc.CompleteAdding();
}
}
}
ThepipelinesteplogicisdefinedinsidethePipelineWorkerAsyncclass.Wehavecreatedtheworkerinstance,providingitwiththeinputcollectionsandatransformationfunctionthatgetsaninitialvalueandcalculatestheresult.Thenwerancollectionprocessinginparallel.Whileweprocessedeachcollection,wepassedcalculationresultstotheoutputcollectionsofthenextstepinourpipeline.Thishappensuntilthefinalstephasbeenreached,whichjustprintsresultstotheconsole.
www.EBooksWorld.ir
![Page 266: Mastering C# Concurrency - EBooksWorld...a print book customer, you are entitled to a discount on the eBook copy. Get in touch with us at for more details](https://reader035.vdocument.in/reader035/viewer/2022062917/5ed62e6504e9cb4adb670aed/html5/thumbnails/266.jpg)
www.EBooksWorld.ir
![Page 267: Mastering C# Concurrency - EBooksWorld...a print book customer, you are entitled to a discount on the eBook copy. Get in touch with us at for more details](https://reader035.vdocument.in/reader035/viewer/2022062917/5ed62e6504e9cb4adb670aed/html5/thumbnails/267.jpg)
SummaryInthischapter,wehaveconsideredthedifferentkindsofasynchronousprogrammingpatterns—fromthesmallestonessuchasataskwithtimeouttothelargemultipurposeparallelpipelinepattern.Wehavereviewedthehistoryofasynchronousprogramminginthe.NETFrameworkandC#,andwentstepbystepthroughallexistingpatternsincludingAPM,EAP,andTAP.
Inthenextchapter,wewillcoveraveryimportanttopicofserver-sideasynchronousprogramming.Wewilllearnaboutscalability,performancemetrics,detailsofIO-boundandCPU-boundasynchronousoperations,andhowtheslightestmistakecanruinyourbackend.Also,wewilllearnacoupleoftricksthatwillallowustodetectpossiblescalabilityproblemsandavoidthem.
www.EBooksWorld.ir
![Page 268: Mastering C# Concurrency - EBooksWorld...a print book customer, you are entitled to a discount on the eBook copy. Get in touch with us at for more details](https://reader035.vdocument.in/reader035/viewer/2022062917/5ed62e6504e9cb4adb670aed/html5/thumbnails/268.jpg)
www.EBooksWorld.ir
![Page 269: Mastering C# Concurrency - EBooksWorld...a print book customer, you are entitled to a discount on the eBook copy. Get in touch with us at for more details](https://reader035.vdocument.in/reader035/viewer/2022062917/5ed62e6504e9cb4adb670aed/html5/thumbnails/269.jpg)
Chapter8.Server-sideAsynchronyInthischapter,wewillshowhowaserverapplicationisdifferentfromotherapplications,whatscalabilityis,andhowitisimportant.Wewilllookatthe.NETHTTPAPIserverapplicationframework,learntouseVisualStudiotocreateloadtests,digintoasynchronousI/Odetails,andreviewimportantnuancessuchassynchronizationcontext.Finally,wewillsuggestanarchitecturalpatternforaserverapplicationtorunlongoperationsandremainscalableandperformant.
www.EBooksWorld.ir
![Page 270: Mastering C# Concurrency - EBooksWorld...a print book customer, you are entitled to a discount on the eBook copy. Get in touch with us at for more details](https://reader035.vdocument.in/reader035/viewer/2022062917/5ed62e6504e9cb4adb670aed/html5/thumbnails/270.jpg)
ServerapplicationsAserverapplicationcanbedefinedasanapplicationthatacceptsrequests,processesthem,andsendsthecorrespondingresponsestotheclient.Communicationhappensviasometransportprotocols,andusually,butnotnecessarily,theclientandserverapplicationsaresituatedondifferentphysicalcomputers.Thecomputerthatrunstheserverapplicationisusuallyreferredtoastheserver.
Therearemanytypesofserverapplications.Forexample,aRemoteDesktopServicessoftwarethatallowsustoopenremotesessiontoaWindowsmachineisaserverapplication.Eachuserconnectionconsumesalotofserverresources,butinthisparticularscenario,thisisinevitable.Thisserverapplicationdoesnotneedtosupporthundredsorthousandsofsimultaneoususersandisintendedtobelikethis.However,ifweimagineawebsitethatallowsonlyafewuserstobrowseitsimultaneously,itwouldbedefinitelyafailure.
Ontheotherhand,itisOKwhenawebsiteusergetsnotificationsfromtheserverwithadelayof2-3seconds,butifwetrytoworkwitharemotedesktopconnectionthatshowsupdatesfromtheserverwithsuchadelay,itwouldbeveryuncomfortable.Therearedifferentmetricsthatcharacterizeaserverapplication,andindifferentscenariosdifferentmetricsareimportant.Oneofthemostimportantserverapplicationcharacteristicsisscalability.HereishowthistermisdefinedinWikipedia:
Scalabilityistheabilityofasystem,network,orprocesstohandleagrowingamountofworkinacapablemanneroritsabilitytobeenlargedtoaccommodatethatgrowth.
Imaginethatwehaveawebsiteandithandlesacertainnumberofconcurrentusers.Tohandlemoreusers,wecantrytoaddmorememoryandmaybeinstallanewCPUwithmorecorestotheserver.Ifthisallowsustoachievethisgoal,wecansaythattheapplicationisabletoscalevertically.Ifwecaninstallmoreserversandmakeourapplicationrunonmultiplemachinesandhandlemoreusers,thiskindofscalabilityiscalledhorizontalscalability.Thefollowingdiagramshowstheverticalandhorizontalscalability:
www.EBooksWorld.ir
![Page 271: Mastering C# Concurrency - EBooksWorld...a print book customer, you are entitled to a discount on the eBook copy. Get in touch with us at for more details](https://reader035.vdocument.in/reader035/viewer/2022062917/5ed62e6504e9cb4adb670aed/html5/thumbnails/271.jpg)
Itmayseemthateveryserverapplicationshouldscaleinbothways,butusuallythisisnotwhathappens.Thistopicisveryinterestingandvast,anditisworthwritinganotherbookonthis.Let’sstatethatmostgeneral-purposeserverapplicationsnowadaysarewebapplicationsandservices,solaterwewillreviewtheASP.NETwebplatformandspecificallytheOWINWebAPIframework.
www.EBooksWorld.ir
![Page 272: Mastering C# Concurrency - EBooksWorld...a print book customer, you are entitled to a discount on the eBook copy. Get in touch with us at for more details](https://reader035.vdocument.in/reader035/viewer/2022062917/5ed62e6504e9cb4adb670aed/html5/thumbnails/272.jpg)
www.EBooksWorld.ir
![Page 273: Mastering C# Concurrency - EBooksWorld...a print book customer, you are entitled to a discount on the eBook copy. Get in touch with us at for more details](https://reader035.vdocument.in/reader035/viewer/2022062917/5ed62e6504e9cb4adb670aed/html5/thumbnails/273.jpg)
TheOWINWebAPIframeworkInthischapter,wewillconcentrateontheASP.NETplatform.Atthetimeofwritingthisbook,ASP.NET5wasnotreleased.However,theOWINprojectexisted,andthecodelookedalmostthesameasinASP.NET5.Sothiswasusedtowritethesampleserverapplications.WhenASP.NET5willbereleased,itwillbeeasytoconvertthiscodetothenewplatform.WewillnotgointothedetailsofOWIN;itisanacronymforOpenWebInterfacefor.NET,andbasically,itisawaytocomposeapplicationcomponentswitheachother.ItisapartoftheASP.NETecosystem,andallweneedtoknowfornowisthatwithOWINwecanwriteHTTPservices.
WhenweuseASP.NET,weseeatypicalHTTPapplicationplatform.First,thereisanHTTPhostthatacceptsincomingconnectionsfromclients.ItcanbeafullInternetInformationServiceswebserver,oritcanbeasimpleHTTPlistenerhostedinausual.NETprocess.AftertheincomingHTTPrequestisprocessedbytheHTTPhost,itgoestotheASP.NETinfrastructure.Itgetsaworkerthreadfromthe.NETthreadpoolandstartsrequestdataprocessingonthisthread.
First,ittriestodefinewhatcodewillbehandlingthisrequestbymatchingtherequestURLtoexistingroutes.AroutedescribeshowURLpartscorrelatetowebapplicationcodeparts.Alogicalsetofservercodeiscalledacontroller.IntheOWINWebAPIframework,acontrollercontainsanumberofactions—methodsthathandledifferentHTTPrequestsusuallybyHTTPverbs(orbyotherrulesthatcanbesetinroutes).Beforeallthisbecomestoocomplicated,let’slookatthecode.Inthesamplesdirectory,itislocatedintheChapter8solutionfolderintheAsyncServerproject.ToleverageOWIN,weneedtoinstalltheMicrosoft.AspNet.WebApi.OwinSelfHostNuGetpackage.ThefirstpartistheentrycodefortheentireOWINapplication:
publicclassStartup
{
publicvoidConfiguration(IAppBuilderappBuilder)
{
varconfig=newHttpConfiguration();
config.Routes.MapHttpRoute(
"DefaultApi",
"api/{controller}/{id}",
new{id=RouteParameter.Optional});
appBuilder.UseWebApi(config);
}
}
HerewehaveconfiguredourOWINapplicationbyprovidingadefaultroute.ItwillmatchURLssuchashttp://hostname/api/somename/5toaclasscalledSomenameControllerthatcontainstheGetmethod(iftherequestverbwasHTTPGET)andwillcallthismethodprovidingaparameterid=5intoit.ThelastlineinstructsOWINtouseaWebAPIcomponentintheapplication.
Nowlet’slookatthecontroller:
publicclassBadAsyncController:ApiController
www.EBooksWorld.ir
![Page 274: Mastering C# Concurrency - EBooksWorld...a print book customer, you are entitled to a discount on the eBook copy. Get in touch with us at for more details](https://reader035.vdocument.in/reader035/viewer/2022062917/5ed62e6504e9cb4adb670aed/html5/thumbnails/274.jpg)
{
privatereadonlyAsyncLib_client;
publicBadAsyncController()
{
_client=newAsyncLib();
}
publicasyncTask<HttpResponseMessage>Get()
{
varsw=Stopwatch.StartNew();
stringvalue=await_client.BadMethodAsync();
sw.Stop();
vartimespan=sw.Elapsed;
returnRequest.CreateResponse(HttpStatusCode.OK,
new
{
Message=value,
Time=timespan
});
}
}
Here,weseetheGetmethodcode,whichcallsalibrary’sasynchronousmethodandmeasuresthetimeittooktocomplete.Thenitreturnsananonymousobjectcontainingtheresponsedata.ItwillbeserializedtotheJSONformatbydefault.
Wewilldefineanothercontroller,whichwillbedifferentonlywithrespecttoanasynchronouslibrary’smethodnamethatitcalls:
publicclassGoodAsyncController:ApiController
{
privatereadonlyAsyncLib_client;
publicGoodAsyncController()
{
_client=newAsyncLib();
}
publicasyncTask<HttpResponseMessage>Get()
{
varsw=Stopwatch.StartNew();
stringvalue=await_client.GoodMethodAsync();
sw.Stop();
vartimespan=sw.Elapsed;
returnRequest.CreateResponse(HttpStatusCode.OK,
new
{
Message=value,
Time=timespan
});
}
}
HerewehavecalledGoodMethodAsync.Wewilldescribebothcontrollerslater,butnowweneedtoruntheapplication.Weneedtocreateanapplicationhost:
www.EBooksWorld.ir
![Page 275: Mastering C# Concurrency - EBooksWorld...a print book customer, you are entitled to a discount on the eBook copy. Get in touch with us at for more details](https://reader035.vdocument.in/reader035/viewer/2022062917/5ed62e6504e9cb4adb670aed/html5/thumbnails/275.jpg)
classProgram
{
staticvoidMain(string[]args)
{
stringbaseAddress="http://localhost:9000/";
using(WebApp.Start<Startup>(url:baseAddress))
{
HttpClientclient=newHttpClient();
varresponse=client.GetAsync(baseAddress+"api/GoodAsync").Result;
Console.WriteLine(response);
Console.WriteLine(response.Content.ReadAsStringAsync().Result);
Console.WriteLine();
response=client.GetAsync(baseAddress+"api/BadAsync").Result;
Console.WriteLine(response);
Console.WriteLine(response.Content.ReadAsStringAsync().Result);
Console.ReadLine();
}
}
}
Thiscodestartsawebapplicationonalocalhostonport9000.Ifthisportisalreadytaken,therewillbeanexception.Inthiscase,justchangetheportnumber.Aftertheapplicationstarts,wewillissuetwoHTTPrequestsandseetheresultsontheconsolewindow.Bothrequestsshouldcompletewithoutanyissuesandshowthatittooktwosecondsforthemtocomplete.Youcanusearegularwebbrowsertoopenhttp://localhost:9000andseetheresults.InternetExplorerisnotverygoodwithJSON,butGoogleChromewillshowyouaJSONresultwithgoodformatting.Besidesthis,thereisaveryusefulGoogleChromeextensioncalledPostman.ThiscanissuedifferentHTTPrequestsandisverycomfortabletouse;itisshowninthefollowingscreenshot:
www.EBooksWorld.ir
![Page 276: Mastering C# Concurrency - EBooksWorld...a print book customer, you are entitled to a discount on the eBook copy. Get in touch with us at for more details](https://reader035.vdocument.in/reader035/viewer/2022062917/5ed62e6504e9cb4adb670aed/html5/thumbnails/276.jpg)
www.EBooksWorld.ir
![Page 277: Mastering C# Concurrency - EBooksWorld...a print book customer, you are entitled to a discount on the eBook copy. Get in touch with us at for more details](https://reader035.vdocument.in/reader035/viewer/2022062917/5ed62e6504e9cb4adb670aed/html5/thumbnails/277.jpg)
www.EBooksWorld.ir
![Page 278: Mastering C# Concurrency - EBooksWorld...a print book customer, you are entitled to a discount on the eBook copy. Get in touch with us at for more details](https://reader035.vdocument.in/reader035/viewer/2022062917/5ed62e6504e9cb4adb670aed/html5/thumbnails/278.jpg)
LoadtestingandscalabilityWeseethatboththecontrollershavebehavedthesamesofar.However,whataboutscalability?Tocheckhowwelltheapplicationscales,weneedtohavemanyrequestsfrommanyusers.Wewillbeabletodothiswiththehelpofdifferenttools.First,wecanuseVisualStudio,butitrequirestheUltimateedition(ortheEnterpriseeditionforVisualStudio2015)wherewebtesttoolsareavailable.Ifyouhaveit,thenyoucancreateanewprojectandchoosetheWebPerformanceandLoadTestprojectfromthetestcategory.Inthesamplesfolder,thereisanalreadycreatedtestprojectthatiscalledAsyncServerTests.NowweneedtocreateWebPerformanceTest.Aftercreating,itwillruninthebrowserandtrytorecordyourtest.Youcanrecorditfromthebrowserorstoptherecordingandaddanewrequestasshowninthefollowingscreenshot;theninPropertiesprovideafullURLtoseewhathavewetestedsofar:
Next,weneedtocreatealoadtest.Whenweaddanewloadtest,itwillshowawizardwithdifferentoptions.Weneedtochooseaconstantloadpatternandsetthenumberofusersto1000.Then,inthetestmix,wehavetopickawebtestthatwehavejustcreated.Finally,intherunsettingssetthewarm-updurationto15seconds,andsetdurationtimeto2minutes.ClickonFinishandrepeatallthisforanothercontroller.Wheneverythingisset,let’srunaloadtestforGoodAsyncController.Theoutputisasshowninthefollowingscreenshot:
www.EBooksWorld.ir
![Page 279: Mastering C# Concurrency - EBooksWorld...a print book customer, you are entitled to a discount on the eBook copy. Get in touch with us at for more details](https://reader035.vdocument.in/reader035/viewer/2022062917/5ed62e6504e9cb4adb670aed/html5/thumbnails/279.jpg)
Thesedatashowthatfor1000userspersecond,theaverageresponsetimewasstillabouttwoseconds.ThisisaverygoodresultandshowsthatGoodAsyncControllerscaleswellandisabletohandlemanyconcurrentrequests.TocomparethistoBadAsyncController,weneedtocreateawebandloadtestforthis,andthenruntheloadtest.
Beforedoingso,ifwedonothavetheVisualStudioUltimateedition,itisstillpossibletoloadtestourwebapplication.TheeasiestwayistousetheApachebenchcommandlinetool.ItisincludedintheApachewebserverinstallation,butifyoudonotneedit,youcandownloadxampp(apreconfiguredApachedistributive)thathasaportableinstallationoption.Thismeansthatyoucandownloadaziparchivefromthexamppsite,andthenextractittosomewhereinyourfilesystem.Youwillfindtheab.exetoolinthexampp\apache\binfolder.Ithasmanyparameters,butwecanusejusttwoofthem—thenumberofconcurrentrequestsandthetimeforthebenchmark.Herewehaveissued1,000concurrentrequestsfor2minutestoourGoodAsyncController:
ab-c1000-t120http://localhost:9000/api/GoodAsync
Theoutputshownwillbethesame—theaveragerequesttimewillbearound2seconds.
Nowlet’sseetheperformancetestresultsforBadAsyncController.Thefollowingscreenshotshowstheperformancetestresults:
www.EBooksWorld.ir
![Page 280: Mastering C# Concurrency - EBooksWorld...a print book customer, you are entitled to a discount on the eBook copy. Get in touch with us at for more details](https://reader035.vdocument.in/reader035/viewer/2022062917/5ed62e6504e9cb4adb670aed/html5/thumbnails/280.jpg)
Herethepictureisdifferent.Weseethattheaveragerequesttimeismorethantentimeshigherthanintheprevioustest.Obviously,thiscodedoesnotscaleaswellasGoodAsyncControllerdoes.Sincethecodesinsidethecontrollersareidentical,andtheonlydifferenceistheasynchronouslibrarymethodthatwascalled,itmakessensetolookintothislibraryandseewhatisgoingon:
publicclassAsyncLib
{
publicasyncTask<string>GoodMethodAsync()
{
awaitTask.Delay(TimeSpan.FromSeconds(2));
return"Goodasynclibrarymethodresult";
}
publicasyncTask<string>BadMethodAsync()
{
Thread.Sleep(TimeSpan.FromSeconds(2));
return"Badasynclibrarymethodresult";
}
}
Thecodeisactuallyverysimple.Boththemethodswaitfortwoseconds,andthenreturnstringresults.However,wecanseethatThread.Sleepisobviouslythereasonbehindbadscalability.Inthediagram,youcanseewhatisgoingonwhenweuseBadMethodAsyncinourwebapplication:
www.EBooksWorld.ir
![Page 281: Mastering C# Concurrency - EBooksWorld...a print book customer, you are entitled to a discount on the eBook copy. Get in touch with us at for more details](https://reader035.vdocument.in/reader035/viewer/2022062917/5ed62e6504e9cb4adb670aed/html5/thumbnails/281.jpg)
Eachworkerthreadstartsrunningourcodeandwaitstwosecondsdoingnothing.Then,theyreturntheresponse.Aswemayrecallfromthepreviouschapters,threadpoolworkerthreadsarealimitedresource,andwhenwestartissuing1,000concurrentrequestsinashorttime,alltheworkerthreadsbecomeoccupiedrunningThread.Sleep.Atthesametime,GoodAsyncControllerbehavesdifferently.Thiscanbeseeninthefollowingdiagram:
Task.Delayusesatimerobjectunderthehood.ThisallowsanASP.NETworkerthreadtostartawaitoperation,andthentoreturntotheapplicationpoolandprocesssomeother
www.EBooksWorld.ir
![Page 282: Mastering C# Concurrency - EBooksWorld...a print book customer, you are entitled to a discount on the eBook copy. Get in touch with us at for more details](https://reader035.vdocument.in/reader035/viewer/2022062917/5ed62e6504e9cb4adb670aed/html5/thumbnails/282.jpg)
requests.Whentwosecondspass,thetimerpostsacontinuationcallbacktoanavailableASP.NETthreadpoolworkerthread.Thisallowstheapplicationtoprocessmoreuserrequests,sinceworkerthreadsarenotblocked.So,thistimerobjecthelpsourapplicationtoremainfastandscalable.
www.EBooksWorld.ir
![Page 283: Mastering C# Concurrency - EBooksWorld...a print book customer, you are entitled to a discount on the eBook copy. Get in touch with us at for more details](https://reader035.vdocument.in/reader035/viewer/2022062917/5ed62e6504e9cb4adb670aed/html5/thumbnails/283.jpg)
www.EBooksWorld.ir
![Page 284: Mastering C# Concurrency - EBooksWorld...a print book customer, you are entitled to a discount on the eBook copy. Get in touch with us at for more details](https://reader035.vdocument.in/reader035/viewer/2022062917/5ed62e6504e9cb4adb670aed/html5/thumbnails/284.jpg)
I/OandCPU-boundtasksIfweconsideranyCPU-intensiveworkthatourserverapplicationcanruninsteadofThread.Sleep,wewillfindthatthisapplicationwillsufferfromthesameproblem.Workerthreadswillbecomebusyquitequickly,andthereisnotmuchthatwecandoaboutthis.Wecantrytochangeourapplicationlogictoworkaroundthisproblem,andwewillgetbacktothisproblemattheendofthechapter.
However,besidesCPU-boundoperations,therearetasksrelatedtoinput/outputprocesses,suchasreadingorwritingafile,issuinganetworkrequest,orevenperformingaqueryagainstadatabase.TheseoperationsusuallytakemuchmoretimecomparedtoCPU-boundwork,andpotentiallytheyshouldbemoreproblematictoourserverapplication.I/O-boundworkcantakeseconds.Sodoesthismeanthatourworkerthreadswillbelockedforalongertimeandtheapplicationwillfailtoscale?
Fortunately,thereisonemorecomponentoftheI/O-boundoperation.Whenwementionafileornetworkrequest,weknowthattherearephysicaldevicessuchasdisksandnetworkcardsthatactuallyexecutetheseoperations.Thesedeviceshavecontrollers,andacontrollerinthiscontextmeansamicro-computerwithitsownCPU.ToperformanI/O-boundtask,wedonotneedtowastethemainCPU’stime,itisenoughtogivealltherequireddatatotheI/Odevicecontroller,anditwillperformtheI/Ooperationandreturntheresultswiththehelpofadevicedriver.
TocommunicatewiththeI/Odevices,WindowsusesaspecialobjectcalledI/OCompletionPort(orIOCP).Itbehavesprettymuchlikeatimer,butthesignalsarecomingfromtheI/Odevicesandnotfromtheinternalclock.Thismeansthat,whileanI/Ooperationisinprogress,wecanreusetheASP.NETworkerthreadtoserveotherrequests,andthusachievegoodscalability.Thefollowingdiagramdepictstheprocessesgraphically:
www.EBooksWorld.ir
![Page 285: Mastering C# Concurrency - EBooksWorld...a print book customer, you are entitled to a discount on the eBook copy. Get in touch with us at for more details](https://reader035.vdocument.in/reader035/viewer/2022062917/5ed62e6504e9cb4adb670aed/html5/thumbnails/285.jpg)
NoticeanewentitycalledtheI/Othreadintheprecedingdiagram.ThereisaseparatesmallerpoolofI/Othreadsinsidethis.NETthreadpool.TheI/Othreadsarenotdifferentfromtheusualworkerthreads,buttheyarebeingusedonlytoexecutecontinuationcallbacksforasynchronousI/Ooperations.Ifweusegeneralworkerthreadsforthispurpose,itcanhappenthattherearenoworkerthreadsavailableandwecannotcompletetheI/Ooperation,whichinturnwillleadtodeadlocks.Usingaseparatethreadpoolwillhelptopreventthis,butwealsoneedtobeverycarefulnottocauseI/Othreadsstarvation.Lookatthefollowingexample.
HerewewillcreateanHTTPGETrequestfortheGooglesite.Aswehavealreadylearned,whenweuseawait,allthecodefollowingthelinewithawaitgetswrappedinacontinuationcallbackandiscalledwhentheasynchronousoperationcompletes.HerewewilluseThread.Sleeptoseewhichthreadswillgetbusy:
privatestaticasyncTask<string>IssueHttpRequest()
{
varstr=awaitnewHttpClient().GetStringAsync("http://google.com");
Thread.Sleep(5000);
returnstr;
}
Then,weneedtogetinformationaboutwhatishappeningwiththreadpoolthreads.Fortunately,a.NETthreadpoolhasasetofstaticmethodsthatallowustogetsomeinformationaboutworkerandI/Othreadsinathreadpool:
privatestaticvoidPrintThreadCounts()
{
intioThreads;
intmaxIoThreads;
intworkerThreads;
intmaxWorkerThreads;
ThreadPool.GetMaxThreads(outmaxWorkerThreads,outmaxIoThreads);
ThreadPool.GetAvailableThreads(outworkerThreads,outioThreads);
Console.WriteLine(
"Workerthreads:{0},I/Othreads:{1},Totalthreads:{2}",
maxWorkerThreads-workerThreads,
maxIoThreads-ioThreads,
Process.GetCurrentProcess().Threads.Count
);
}
IntheMainmethod,wewillrunmanyasynchronousI/Otasks;whileiteratingthroughallthesetaskstocompleteineachsecond,wewillprintoutinformationaboutthreadpoolthreads:
privatestaticvoidMain(string[]args)
{
vartasks=newList<Task<string>>();
for(vari=0;i<100;i++)
{
tasks.Add(Task.Run(()=>
www.EBooksWorld.ir
![Page 286: Mastering C# Concurrency - EBooksWorld...a print book customer, you are entitled to a discount on the eBook copy. Get in touch with us at for more details](https://reader035.vdocument.in/reader035/viewer/2022062917/5ed62e6504e9cb4adb670aed/html5/thumbnails/286.jpg)
{
//Thread.Sleep(5000);
returnIssueHttpRequest();
}));
}
varallComplete=Task.WhenAll(tasks);
while(allComplete.Status!=TaskStatus.RanToCompletion)
{
Thread.Sleep(1000);
PrintThreadCounts();
}
Console.WriteLine(tasks[0].Result.Substring(0,160));
}
Ifwerunthiscode(amongtheothersamplesforChapter8;thisoneiscalledIOThreadsTest),itwillshowthattheI/Othreadnumberwillslowlyincreaseuntilsomepointandgobacktozero.ToprovethattheI/Ooperationreallyhappens,thelastlineswillbethebeginningoftheGooglewebpageHTMLcontent.IfwenowcommentoutthefirstThread.SleepcallanduncommentitintheMainmethod,thesituationwillbedifferent.Wewillblockworkerthreads,andtheI/Othreadnumberwillremainlow.
www.EBooksWorld.ir
![Page 287: Mastering C# Concurrency - EBooksWorld...a print book customer, you are entitled to a discount on the eBook copy. Get in touch with us at for more details](https://reader035.vdocument.in/reader035/viewer/2022062917/5ed62e6504e9cb4adb670aed/html5/thumbnails/287.jpg)
www.EBooksWorld.ir
![Page 288: Mastering C# Concurrency - EBooksWorld...a print book customer, you are entitled to a discount on the eBook copy. Get in touch with us at for more details](https://reader035.vdocument.in/reader035/viewer/2022062917/5ed62e6504e9cb4adb670aed/html5/thumbnails/288.jpg)
DeepdiveintoasynchronousI/OUsually,thereisnoneedtouseWin32APItostartanasynchronousI/Ooperation.The.NETbaseclasslibraryhasmanyAPIsthatarecomfortabletouse,andleverageasynchronousI/O.Thefollowingcodeisnotintendedtobeusedinaproductionsoftware,itjustshowshowsuchanAPIcanbewrittenincaseyoudonothaveitinthe.NETFramework.
First,weneedtoallowanunsafecodeinourproject.ThesettingisinsidetheprojectpropertiesoftheBuildsectionasshowninthefollowingscreenshot:
Here,weneedtodefinemanydatastructuresfortheAPIfunctioncalls.ThefullyworkingcodecanbefoundintheBindHandlesampleproject.Inthisbook,wewillskiptheunimportantdetails.
First,weneedtouseP/InvokefortwoWindowsAPIfunctions:
[DllImport("kernel32.dll",SetLastError=true,CharSet=CharSet.Auto)]
publicstaticexternSafeFileHandleCreateFile(
stringlpFileName,
EFileAccessdwDesiredAccess,
EFileSharedwShareMode,
IntPtrlpSecurityAttributes,
ECreationDispositiondwCreationDisposition,
EFileAttributesdwFlagsAndAttributes,
SafeFileHandlehTemplateFile);
[DllImport("kernel32.dll",SetLastError=true)]
unsafeinternalstaticexternintReadFile(
SafeFileHandlehandle,
byte*bytes,
intnumBytesToRead,
www.EBooksWorld.ir
![Page 289: Mastering C# Concurrency - EBooksWorld...a print book customer, you are entitled to a discount on the eBook copy. Get in touch with us at for more details](https://reader035.vdocument.in/reader035/viewer/2022062917/5ed62e6504e9cb4adb670aed/html5/thumbnails/289.jpg)
IntPtrnumBytesRead_mustBeZero,
NativeOverlapped*overlapped);
Then,wecreateafileandwritesometextinitintheusualway:
using(varsw=File.CreateText("test.txt"))
{
sw.WriteLine("Test!");
}
Here,weareopeningthisfileforasynchronousreading.NoticeEFileAttributes.Overlappedinthemethodparameters.IfwewantanasynchronousI/Ooperation,wemustspecifythisflag:
SafeFileHandlehandle=CreateFile(
"test.txt",
EFileAccess.FILE_GENERIC_READ,
EFileShare.Read|EFileShare.Write|EFileShare.Delete,
(IntPtr)null,
ECreationDisposition.OpenExisting,
EFileAttributes.Overlapped,
newSafeFileHandle(IntPtr.Zero,false));
Nowwebindthefilehandletoa.NETthreadpool.ItmaintainsanI/Ocompletionport,andthishandlewillbeattachedtotheport:
if(!ThreadPool.BindHandle(handle))
{
Console.WriteLine("Failedtobindhandletothethreadpool.");
return;
}
Weneedtoprepareabufferforthefilethatisgoingtoberead.Thefollowingcodecheckswhetherthebufferisempty:
byte[]bytes=newbyte[0x8000];
Console.WriteLine("Firstbyteinbuffer:{0}",bytes[0]);
Now,weneedtoprepareacallbackthatwillbeexecutedaftertheasynchronousoperationcompletes.Ifeverythingisfine,wewillgetfilecontentfromthebufferandprintittotheconsole.Wemustcleanuptheresourcesaftertheoperationcompletion:
IOCompletionCallbackiocomplete=delegate(uinterrorCode,uintnumBytes,
NativeOverlapped*nativeOverlapped)
{
try
{
if(errorCode!=0&&numBytes!=0)
{
Console.WriteLine("Error{0}whenreadingfile.",errorCode);
}
Console.WriteLine("Read{0}bytes.",numBytes);
Console.WriteLine(
Encoding.UTF8.GetChars(
newArraySegment<byte>(bytes,0,(int)numBytes).ToArray()));
}
www.EBooksWorld.ir
![Page 290: Mastering C# Concurrency - EBooksWorld...a print book customer, you are entitled to a discount on the eBook copy. Get in touch with us at for more details](https://reader035.vdocument.in/reader035/viewer/2022062917/5ed62e6504e9cb4adb670aed/html5/thumbnails/290.jpg)
finally
{
Overlapped.Unpack(nativeOverlapped);
Overlapped.Free(nativeOverlapped);
}
};
Here,wehavepreparedadatastructuretobepassedtotheasynchronousoperationstart.Wehavetopinourbuffer’saddresstomemory,sothepointerwillbevalid:
Overlappedoverlapped=newOverlapped();
NativeOverlapped*pOverlapped=overlapped.Pack(iocomplete,bytes);
pOverlapped->OffsetLow=0;
fixed(byte*p=bytes)
{
//Herewestartasynchronouslyreadingthefile.
//Whentheoperationwillcomplete,ioComplete
//callbackwillbecalled
intr=ReadFile(handle,p,bytes.Length,IntPtr.Zero,pOverlapped);
if(r==0)
{
r=Marshal.GetLastWin32Error();
if(r!=ERROR_IO_PENDING)
{
Console.WriteLine("Failedtoreadfile.LastErroris{0}",
Marshal.GetLastWin32Error());
Overlapped.Unpack(pOverlapped);
Overlapped.Free(pOverlapped);
return;
}
}
}
Whenwerunthiscode,wewillseethatthefilecontenthasbeensuccessfullyread.
www.EBooksWorld.ir
![Page 291: Mastering C# Concurrency - EBooksWorld...a print book customer, you are entitled to a discount on the eBook copy. Get in touch with us at for more details](https://reader035.vdocument.in/reader035/viewer/2022062917/5ed62e6504e9cb4adb670aed/html5/thumbnails/291.jpg)
www.EBooksWorld.ir
![Page 292: Mastering C# Concurrency - EBooksWorld...a print book customer, you are entitled to a discount on the eBook copy. Get in touch with us at for more details](https://reader035.vdocument.in/reader035/viewer/2022062917/5ed62e6504e9cb4adb670aed/html5/thumbnails/292.jpg)
RealandfakeasynchronousI/OoperationsSofar,anasynchronousI/Oseemstobeagoodthingforserverapplications.Unfortunately,thereisquiteunexpectedunderwaterstonethatisveryhardtofind.Let’slookatthefollowingcode.IthappensthattheFileStreaminstancehastheIsAsyncproperty,indicatingthattheunderlyingI/Ooperationisasynchronous.Wewillstartafewasynchronouswritesandcheckwhethertheyarereallyasynchronous:
privateconstintBUFFER_SIZE=4096;
privatestaticasyncTaskProcessAsynchronousIO()
{
using(varstream=newFileStream("test1.txt",FileMode.Create,
FileAccess.ReadWrite,FileShare.None,BUFFER_SIZE))
{
Console.WriteLine("1.UsesI/OThreads:{0}",stream.IsAsync);
varbuffer=Encoding.UTF8.GetBytes(CreateFileContent());
vart=stream.WriteAsync(buffer,0,buffer.Length);
awaitt;
}
using(varstream=newFileStream("test2.txt",FileMode.Create,
FileAccess.ReadWrite,FileShare.None,BUFFER_SIZE,
FileOptions.Asynchronous))
{
Console.WriteLine("2.UsesI/OThreads:{0}",stream.IsAsync);
varbuffer=Encoding.UTF8.GetBytes(CreateFileContent());
vart=stream.WriteAsync(buffer,0,buffer.Length);
awaitt;
}
using(varstream=File.Create("test3.txt",BUFFER_SIZE,
FileOptions.Asynchronous))
using(varsw=newStreamWriter(stream))
{
Console.WriteLine("3.UsesI/OThreads:{0}",stream.IsAsync);
awaitsw.WriteAsync(CreateFileContent());
}
using(varsw=newStreamWriter("test4.txt",append:true))
{
Console.WriteLine("4.UsesI/OThreads:{0}",((FileStream)
sw.BaseStream).IsAsync);
awaitsw.WriteAsync(CreateFileContent());
}
Console.WriteLine("Deletingfiles…");
vardeleteTasks=newTask[4];
for(vari=0;i<4;i++)
www.EBooksWorld.ir
![Page 293: Mastering C# Concurrency - EBooksWorld...a print book customer, you are entitled to a discount on the eBook copy. Get in touch with us at for more details](https://reader035.vdocument.in/reader035/viewer/2022062917/5ed62e6504e9cb4adb670aed/html5/thumbnails/293.jpg)
{
varfileName=string.Format("test{0}.txt",i+1);
deleteTasks[i]=SimulateAsynchronousDelete(fileName);
}
awaitTask.WhenAll(deleteTasks);
Console.WriteLine("Deletingcomplete.");
}
privatestaticstringCreateFileContent()
{
varsb=newStringBuilder();
for(vari=0;i<100000;i++)
{
sb.AppendFormat("{0}",newRandom(i).Next(0,99999));
sb.AppendLine();
}
returnsb.ToString();
}
privatestaticTaskSimulateAsynchronousDelete(stringfileName)
{
//NodeleteasyncinAPI
returnTask.Run(()=>File.Delete(fileName));
}
privatestaticvoidMain(string[]args)
{
vart=ProcessAsynchronousIO();
t.GetAwaiter().GetResult();
}
Whenwerunthecode,wewillseethatonlythenumberstwoandthreewritesareasynchronous.However,wehaveusedtheawaitstatementandcallWriteAsyncinallcases.Whatisgoingon?TheansweristhatifwedonotspecifythecorrectoptionsforthefileAPIweuse,thefilewillprovideuswiththewrongkindofasynchronythatusesworkerthreadsfortheI/Oprocessandthusisnotscalable.
ThisproblemcanbeillustratedbytheSimulateAsynchronousDeletemethod.ThereisnoasynchronousdeletefunctionintheWin32API,soitjuststartsanewtaskwherethesynchronousdeleteisbeingperformed.Thispracticeiscalledasyncoversyncandshouldbeavoided.Donotwriteyourlibrarieslikethis.IfthereisnoasynchronousAPIforsomeoperation,donotmakeitlookasynchronous.Inthefollowingdiagram,wecanseewhyitisabadpracticeforaserverapplication:
www.EBooksWorld.ir
![Page 294: Mastering C# Concurrency - EBooksWorld...a print book customer, you are entitled to a discount on the eBook copy. Get in touch with us at for more details](https://reader035.vdocument.in/reader035/viewer/2022062917/5ed62e6504e9cb4adb670aed/html5/thumbnails/294.jpg)
Thisworkflowisevenworsethantheusualsynchronouscode,becausethereisanadditionalperformanceoverheadrelatedtorunningthispartoftheoperationonadifferentworkerthread.WeendupwastingworkerthreadfortheentiretimeoftheI/Ooperationanyway,andthisisfakeasynchronousI/O.ItisactuallyaCPU-boundoperationthatwillaffectthescalabilityandperformanceofyourapplication.
Soifwehavethesourcecodeofalibrary,wecanmakesurethatitleveragesatrulyasynchronousI/O.However,asourcecodeisnotalwaysavailable,andevenifitisavailable,itcanoftenbepuzzlingandcomplicated.Tomakesurethatourasynchronyisright,wecanuseatoolthatshowstheAPIcallsfromtheapplication,andwewillbeabletoseewhetheranI/Ocompletionporthasbeenused.
ThereisaprogramcalledAPIMonitor.Itcanbeeasilyfoundinanysearchengine,isfreetouse,andeasytoinstall.Therearetwoversionsofthisprogram:32-bitand64-bit,soyouhavetopayattentiontowhichversionisappropriateforyourapplication.
Fromthestart,wewillneedtosetupafiltertoseeonlytherequiredfunctioncalls.Foroursamplecode,itisenoughtomonitortwofunctions,CreateFileWandCreateIoCompletionPort.Thefilterisshowninthefollowingscreenshot:
www.EBooksWorld.ir
![Page 295: Mastering C# Concurrency - EBooksWorld...a print book customer, you are entitled to a discount on the eBook copy. Get in touch with us at for more details](https://reader035.vdocument.in/reader035/viewer/2022062917/5ed62e6504e9cb4adb670aed/html5/thumbnails/295.jpg)
ThenweneedtorunourapplicationunderAPIMonitor.Tostartmonitoring,pressCtrl+MorusetheFile|MonitorNewProcess…menuoption.Thestartdialogwillappearasfollows:
www.EBooksWorld.ir
![Page 296: Mastering C# Concurrency - EBooksWorld...a print book customer, you are entitled to a discount on the eBook copy. Get in touch with us at for more details](https://reader035.vdocument.in/reader035/viewer/2022062917/5ed62e6504e9cb4adb670aed/html5/thumbnails/296.jpg)
WhenyoupressOK,theapplicationwillstartandthenyouwillseeareportasfollows:
Youcanseethattowritethetest2.txtfile,theFILE_FLAG_OVERLAPPEDflagwasprovidedtotheCreateFileWfunction,meaningthatweareusingtheI/Ocompletionport.TheCreateFileWfunctionreturnedthe0x234filehandle,whichwasboundtotheI/OcompletionportbycallingtheCreateIoCompletionPortfunction.Thefirstandthelastfilewritesarenotusingthecompletionportandthusarenotreallyasynchronous.
www.EBooksWorld.ir
![Page 297: Mastering C# Concurrency - EBooksWorld...a print book customer, you are entitled to a discount on the eBook copy. Get in touch with us at for more details](https://reader035.vdocument.in/reader035/viewer/2022062917/5ed62e6504e9cb4adb670aed/html5/thumbnails/297.jpg)
www.EBooksWorld.ir
![Page 298: Mastering C# Concurrency - EBooksWorld...a print book customer, you are entitled to a discount on the eBook copy. Get in touch with us at for more details](https://reader035.vdocument.in/reader035/viewer/2022062917/5ed62e6504e9cb4adb670aed/html5/thumbnails/298.jpg)
SynchronizationcontextAnotherveryimportantconceptissynchronizationcontext.Wewillreviewsynchronizationcontextandotherkindsofcontextindetailinthenextchapter,butfornowlet’sstartwithademonstration.ThissampleiscalledIISSynchronizationContext.ThistimeweneedtohostourapplicationinanIISwebserver,sowewillusetheMicrosoft.Owin.Host.SystemWebNuGetpackage,andcreateanemptyASP.NETapplication.First,wewillconfigureourapplicationanddefineadefaultroute:
publicclassStartup
{
publicvoidConfiguration(IAppBuilderappBuilder)
{
varconfig=newHttpConfiguration();
config.Routes.MapHttpRoute(
"DefaultApi","api/{controller}/{action}/{id}",new{id=
RouteParameter.Optional}
);
appBuilder.UseWebApi(config);
}
}
Thenwewillcreateacontrollerwithtwomethods.Oneofthemtriestogetasynchronousoperationresultssynchronously,andtheotherusesawaitandasynchronousexecution:
publicclassHomeController:ApiController
{
[HttpGet]
publicintSync()
{
varlib=newAsyncLib();
returnlib.CountCharactersAsync(newUri("http://google.com")).Result;
}
[HttpGet]
publicasyncTask<int>Async()
{
varlib=newAsyncLib();
returnawaitlib.CountCharactersAsync(newUri("http://google.com"));
}
}
HerewehavedefinedourasynchronousoperationasdownloadingcontentfromagivenURLandreturningitslengthincharacters:
publicclassAsyncLib
{
publicasyncTask<int>CountCharactersAsync(Uriuri)
{
using(varclient=newHttpClient())
{
www.EBooksWorld.ir
![Page 299: Mastering C# Concurrency - EBooksWorld...a print book customer, you are entitled to a discount on the eBook copy. Get in touch with us at for more details](https://reader035.vdocument.in/reader035/viewer/2022062917/5ed62e6504e9cb4adb670aed/html5/thumbnails/299.jpg)
varcontent=awaitclient.GetStringAsync(uri)
//.ConfigureAwait(continueOnCapturedContext:false);
returncontent.Length;
}
}
}
WhenwerunthiscodeinVisualStudio,adefaultwebbrowsershouldstartandopenthewebapplicationURL.Inthesamplecode,bothactionscanbereachedviahttp://localhost:5098/api/Home/Asyncandhttp://localhost:5098/api/Home/Syncrespectively.TheAsyncversionworksfine,whiletheSynccodehangsforever.
ThiscanbefixedifweuncommenttheConfigureAwaitlineintheAsyncLibclass.Ifyourunanewcode,theSyncversionwillalsowork.Tounderstandthereasonsforthis,weneedtogetbacktothesynchronizationcontextconcept.Asynchronizationcontextrepresentsanenvironmentthathassomedataassociatedtoit,andanabilitytorunadelegateusingthisenvironment.InASP.NET,whenusingaIISwebserverthereisaspecialsynchronizationcontextthatkeepsthecurrentcultureanduseridentity.
Nowwhenweuseawaitbydefault,ifweuseawaitwiththeTask<T>instance,wewillgetaspecialTaskAwaiter<T>structurethatisusedbytheC#compilerinthegeneratedstatemachinecode.Torunacontinuationcallback,C#endsupcallingtheUnsafeOnCompletedmethod:
publicstructTaskAwaiter<TResult>:ICriticalNotifyCompletion
{
privatereadonlyTask<TResult>m_task;
internalTaskAwaiter(Task<TResult>task)
{
Contract.Requires(task!=null,"Constructinganawaiterrequiresa
tasktoawait.");
m_task=task;
}
publicvoidUnsafeOnCompleted(Actioncontinuation)
{
TaskAwaiter.OnCompletedInternal(m_task,continuation,
continueOnCapturedContext:true,flowExecutionContext:false);
}
}
Sothiscodetriestopostacontinuationcallbacktothecurrentsynchronizationcontext.Howeverwhenwerunthiscodesynchronously,wewillgetaclassicdeadlocksituation:
ThecodeblocksthecurrentsynchronizationcontextuntiltheoperationcompletesTheoperationcompletesandpoststhecontinuationcallbacktothecurrentsynchronizationcontextHowever,itisblockeduntilwegetaresultandcannotrunthiscontinuationcallback,andthuscannotgetaresultAllthisleadstoadeadlock
Topreventthis,wecanusetheConfigureAwait(continueOnCapturedContext:false)
www.EBooksWorld.ir
![Page 300: Mastering C# Concurrency - EBooksWorld...a print book customer, you are entitled to a discount on the eBook copy. Get in touch with us at for more details](https://reader035.vdocument.in/reader035/viewer/2022062917/5ed62e6504e9cb4adb670aed/html5/thumbnails/300.jpg)
method.ItreturnsaspecialConfiguredTaskAwaitabletypeinstance,whichinturnreturnsConfiguredTaskAwaitertotheC#compiler-generatedcode.Inthiscase,weuseUnsafeOnCompletedaswell,butthistimeitisspecificallyconfigurednottocapturethecurrentsynchronizationcontext,andthecontinuationcallbackgetspostedtoadefaulttaskscheduler,whichislikelytobeathreadpoolworkerthread:
publicstructConfiguredTaskAwaiter:ICriticalNotifyCompletion
{
privatereadonlyTask<TResult>m_task;
privatereadonlyboolm_continueOnCapturedContext;
internalConfiguredTaskAwaiter(Task<TResult>task,bool
continueOnCapturedContext)
{
Contract.Requires(task!=null,"Constructinganawaiterrequiresa
tasktoawait.");
m_task=task;
m_continueOnCapturedContext=continueOnCapturedContext;
}
publicvoidUnsafeOnCompleted(Actioncontinuation)
{
OnCompletedInternal(m_task,continuation,m_continueOnCapturedContext,
flowExecutionContext:false);
}
}
Thismeansthatwhenyouwritealibrarywithasyncmethodsthathavetheawaitstatementsinsideandifyouaresurethatyourcontinuationcodedoesnotneedthecurrentsynchronizationcontext,alwaysuse.ConfigureAwait(false)topreventsuchsituations.Alsoviceversa;ifyouhavetoworkwithasynchronousoperationssynchronouslyinASP.NET,itisverydangeroustousetheTask.Resultpropertyandblockthecurrentthread.YoushoulduseTask.ContinueWithalongwiththecorrespondingoptionstogettheresultwithoutawait.
www.EBooksWorld.ir
![Page 301: Mastering C# Concurrency - EBooksWorld...a print book customer, you are entitled to a discount on the eBook copy. Get in touch with us at for more details](https://reader035.vdocument.in/reader035/viewer/2022062917/5ed62e6504e9cb4adb670aed/html5/thumbnails/301.jpg)
www.EBooksWorld.ir
![Page 302: Mastering C# Concurrency - EBooksWorld...a print book customer, you are entitled to a discount on the eBook copy. Get in touch with us at for more details](https://reader035.vdocument.in/reader035/viewer/2022062917/5ed62e6504e9cb4adb670aed/html5/thumbnails/302.jpg)
CPU-boundtasksandqueuesSofar,wehavereviewedmanyspecialdetailsaboutI/O-boundtasks,butwhataboutCPU-boundwork?Technically,themostefficientwaywillbetorunsuchworksynchronouslyandscalehorizontallybyaddingmoreandmoreserverstobeabletohandleincreasingload.Nevertheless,itcanhappenthatthisCPU-boundworkisnottheonlyresponsibilityofaserverapplication.Inthiscase,wecanfindawaytogetthistoworkoutofthewebapplication,allowingittorunfast;nowtheCPU-boundpartcanbescaledseparatelyanddoesnotaffecttherestofthisapplication.
Thisishowcloudapplicationswork.Usually,ifthereisalongrunningoperation,awebapplicationregistersitintosomedatastore,returnsauniqueidentifierofthisoperationtotheclient,andpoststhisoperationtoaspecialqueue.Thenthereisaseparatepoolofworkerprocessesthatmonitorthisqueue,gettasksfromthem,processthem,andwriteresultstoadatastore.Whentheclientarrivesnexttime,thewebapplicationcheckswhetherthetaskhasbeenalreadycompletedbyanyworkerandifithas,theapplicationreturnstheresulttotheclient.
www.EBooksWorld.ir
![Page 303: Mastering C# Concurrency - EBooksWorld...a print book customer, you are entitled to a discount on the eBook copy. Get in touch with us at for more details](https://reader035.vdocument.in/reader035/viewer/2022062917/5ed62e6504e9cb4adb670aed/html5/thumbnails/303.jpg)
www.EBooksWorld.ir
![Page 304: Mastering C# Concurrency - EBooksWorld...a print book customer, you are entitled to a discount on the eBook copy. Get in touch with us at for more details](https://reader035.vdocument.in/reader035/viewer/2022062917/5ed62e6504e9cb4adb670aed/html5/thumbnails/304.jpg)
SummaryInthischapter,wehavelearnedaboutserverapplicationsandhowtheyaredifferent.Wehavelookedatscalabilityandunderstoodwhyitisveryimportantforaserverapplicationtobeabletoscalewell.WehavecreatedanOWINWebAPIapplicationandlearnedtohostitinanIISwebserverandinaseparateprocess.WehaveusedVisualStudiotocreateloadtestsforourserverapplication,checkedwhathappenswhenweusegoodandpoorlywrittenasynchronouscode,andleveragedtheApachebenchcommandlinetooltorunbenchmarkswithoutVisualStudio.
WealsohavereviewedindetailwhatanI/OthreadandanI/Ocompletionportare,andfoundoutreasonswhyusinganasynchronousI/Oisthekeytobuildingscalableserverapplications.Tocheckwhetherathird-partycodeusesrealasynchronousI/O,wehavefoundatoolthatshowsWin32APIcalls.Inconclusion,wehavelearnedaboutsynchronizationcontextandhowwecanconfigurecontinuationtaskstoberunonadefaulttaskscheduler.Finally,wehavediscussedhowtoenhancethescalabilityofaserverapplicationthathaslong-runningCPU-boundtasks.
Inthenextchapter,wewillreviewclientapplications,andspecificallytheuserinterfacepart,indetail.Wewilllearnaboutmodernuserinterfacetechnologies,howtokeeptheUIfastandresponsive,andhowtoavoidcommonpitfallsandmistakeswithasynchronyontheclientside.
www.EBooksWorld.ir
![Page 305: Mastering C# Concurrency - EBooksWorld...a print book customer, you are entitled to a discount on the eBook copy. Get in touch with us at for more details](https://reader035.vdocument.in/reader035/viewer/2022062917/5ed62e6504e9cb4adb670aed/html5/thumbnails/305.jpg)
www.EBooksWorld.ir
![Page 306: Mastering C# Concurrency - EBooksWorld...a print book customer, you are entitled to a discount on the eBook copy. Get in touch with us at for more details](https://reader035.vdocument.in/reader035/viewer/2022062917/5ed62e6504e9cb4adb670aed/html5/thumbnails/306.jpg)
Chapter9.ConcurrencyintheUserInterfaceInthischapter,wewillreviewtheaspectsofusingasynchronyinclientapplications.WewilllearnabouthowaWindowsapplicationworksanddefinewhatanUIthreadandmessageloopis.Whilegoingthroughthedetailsofexecutionandsynchronizationcontexts,wewilldigintoaC#compiler-generatedcodeandseehowitisrelatedtotheuseoftheawaitkeywordinyourprogram.
www.EBooksWorld.ir
![Page 307: Mastering C# Concurrency - EBooksWorld...a print book customer, you are entitled to a discount on the eBook copy. Get in touch with us at for more details](https://reader035.vdocument.in/reader035/viewer/2022062917/5ed62e6504e9cb4adb670aed/html5/thumbnails/307.jpg)
TheimportanceofasynchronyforUIWhileaserverapplicationingeneralhastobescalablebeforeeverythingelse,aclientapplicationisdifferent.Itisusuallyintendedtorunforoneuserononecomputer,andthustheuserexpectsittorunfastandnotcausetroublesfortheotherapplicationsrunninginthesystem.Whilethesecondpartisusuallyhandledbytheoperatingsystem,theapplication’sperformanceingettingthingsdoneandreactingtouserinputiscrucialforapositiveuserexperience.Imagineifyourunanapplicationandithangsforafewminutesafteryouclickonabutton.Agoodapplicationremainsresponsiveandindicatesthatyouhavejuststartedalong-runningoperationthatisstillrunningandisgoingtocompletesoon.Meanwhile,youcandosomethingelse—clickonotherbuttonsandperformsomeothertasks.Whenthetaskiscompleted,youcangetbacktoitandseetheresult.
However,achievingjustthisisoftennotenough.Ifyouusesomeapplicationanditreactstoyourinputevenwithaslightdelay,itwillbestillveryannoying.Itishumannaturetoexpectanimmediatereaction,andevensmalldelayscancauseirritationandanger.ThisrequiresaprogramtooffloadworkfromtheUIasmuchaspossible,andforthiswehavetolearnhowtheUIworksandtheUIthreadingarchitecture.Laterinthischapter,wewillgodeeperintothedetails.
Thelastaspectisnotrelevanttothischapter,butisstillveryimportant.Whileaserverapplicationhastoconsumeasfewresourcesperuseraspossible,ifyourprogramneedscomputationalpowerthenithastobeabletousethenecessaryresources.Forinstance,ifauser’scomputerhasfourcoreCPUswithhyperthreadingtechnology,thentheapplicationhastobeabletouseallthelogicalcorestogettheresultassoonaspossible.Thisiswherethisbook’scontentwillbeveryuseful,especiallyChapter7,LeveragingParallelPatterns,whichprovidesyouwithconcurrentprogrammingpatterns.
www.EBooksWorld.ir
![Page 308: Mastering C# Concurrency - EBooksWorld...a print book customer, you are entitled to a discount on the eBook copy. Get in touch with us at for more details](https://reader035.vdocument.in/reader035/viewer/2022062917/5ed62e6504e9cb4adb670aed/html5/thumbnails/308.jpg)
www.EBooksWorld.ir
![Page 309: Mastering C# Concurrency - EBooksWorld...a print book customer, you are entitled to a discount on the eBook copy. Get in touch with us at for more details](https://reader035.vdocument.in/reader035/viewer/2022062917/5ed62e6504e9cb4adb670aed/html5/thumbnails/309.jpg)
UIthreadsandmessageloopsModernUIframeworkandprogramminglanguagesnotonlymakeclientapplicationdevelopmentmucheasierthanbefore,buttheyalsoraisealevelofabstractionandhideimportantimplementationdetails.TounderstandhowtheUIworks,weshouldlookatthelower-levelcode.
Thefollowingisthecodeofasimplewin32program,whichiswritteninC.IfyourVisualStudiodoesnothaveC/C++projectsupportinstalled,itisnotaproblem.ThiscodeisneededjusttoillustratehowaWindowsapplicationworks,andwe’llbreakitintopartsandexamineeachpartindetail.First,let’slookatthefullprogramcodelisting:
#include<windows.h>
constchar_szClassName[]="ConcurrencyInUIWindowClass";
LRESULTCALLBACKWndProc(HWNDhwnd,UINTmsg,WPARAMwParam,LPARAMlParam)
{
switch(msg)
{
caseWM_CLOSE:
DestroyWindow(hwnd);
break;
caseWM_DESTROY:
PostQuitMessage(0);
break;
default:
returnDefWindowProc(hwnd,msg,wParam,lParam);
}
return0;
}
intWINAPIWinMain(HINSTANCEhInstance,HINSTANCEhPrevInstance,LPSTR
lpCmdLine,intnCmdShow)
{
WNDCLASSEXwc;
HWNDhwnd;
MSGmsg;
//CreatingtheWindowclass
wc.cbSize=sizeof(WNDCLASSEX);//sizeoftheinstance
wc.style=0;//classstyles,notimportanthere
wc.lpfnWndProc=WndProc;//thepointertoWindowprocedure
wc.cbClsExtra=0;//extradataforthisclass
wc.cbWndExtra=0;//extradataforthisWindow
wc.hInstance=hInstance;//applicationinstancehandle
wc.hIcon=LoadIcon(NULL,IDI_APPLICATION);//standardlarge//icon
wc.hCursor=LoadCursor(NULL,IDC_ARROW);//standardarrow//cursor
wc.hbrBackground=(HBRUSH)(COLOR_WINDOW+1);//background//brush
wc.lpszMenuName=NULL;//nameofmenuresource
wc.lpszClassName=_szClassName;//Windowclassname
wc.hIconSm=LoadIcon(NULL,IDI_APPLICATION);//standardsmall//icon
www.EBooksWorld.ir
![Page 310: Mastering C# Concurrency - EBooksWorld...a print book customer, you are entitled to a discount on the eBook copy. Get in touch with us at for more details](https://reader035.vdocument.in/reader035/viewer/2022062917/5ed62e6504e9cb4adb670aed/html5/thumbnails/310.jpg)
if(!RegisterClassEx(&wc))
{
MessageBox(NULL,"Windowclassregistrationfailed!",
"Error!",MB_ICONEXCLAMATION|MB_OK);
return0;
}
hwnd=CreateWindowEx(
WS_EX_CLIENTEDGE,
_szClassName,
"UIConcurrency",
WS_OVERLAPPEDWINDOW,
CW_USEDEFAULT,CW_USEDEFAULT,480,240,
NULL,NULL,hInstance,NULL);
if(hwnd==NULL)
{
MessageBox(NULL,"Windowcreationfailed!",
"Error!",MB_ICONEXCLAMATION|MB_OK);
return0;
}
ShowWindow(hwnd,nCmdShow);
UpdateWindow(hwnd);
while(GetMessage(&msg,NULL,0,0)>0)
{
TranslateMessage(&msg);
DispatchMessage(&msg);
}
returnmsg.wParam;
}
TheentrypointistheWinMainmethod,whichisageneralentrancepointforallWindowsapplications.Thisiswhatwillbecalledwhentheapplicationstarts.Thismethodisquitebig,butbasicallyitconsistsoffourmainsteps.
ThefirststepistocreatetheWindowclassinstance,andprovideitwiththedatarequired.ThemostimportantparthereisthepointertotheWindowprocedure.Inourcase,itistheWndProcmethod,anditwillbeusedlatertoprocessmessagesfromtheoperatingsystem.Also,weneedauniquestringnameforourWindowclasstouseittocreateawindowinourapplication.
ThesecondstepbeginswheretheRegisterClassExmethodiscalled.WeregistertheWindowclassandimmediatelyuseitsnametocreatethemainapplicationwindowusingtheCreateWindowExfunctioncall.Thiscallreturnsahandlethatisneededforalmosteveryoperationrelatedtothiswindow.ThenwedisplaytheapplicationwindowonthescreenusingtheShowWindowandUpdateWindowmethods.
Thethirdstepisveryimportantandevenhighlightedinthecodelisting.Thisiswhatisusuallycalledthemessagelooporthemessagepump.ThiscyclecallstheGetMessagemethodthatgetsthefirstmessagefromthemessagequeue.Thisqueueiscreatedwhena
www.EBooksWorld.ir
![Page 311: Mastering C# Concurrency - EBooksWorld...a print book customer, you are entitled to a discount on the eBook copy. Get in touch with us at for more details](https://reader035.vdocument.in/reader035/viewer/2022062917/5ed62e6504e9cb4adb670aed/html5/thumbnails/311.jpg)
threadcreatesatleastonewindowandthusbecomestheUIthread.Ifthemessagequeueisempty,theGetMessagemethodcallgetsblockeduntilanymessagesappearanditdequeuesthefirstmessage.Theoperatingsystemputsmessagessuchasakeypressoramouseclickonthisqueue,andthenthismessagegetssomepreprocessingbytheTranslateMessagefunction.ThenDispatchMessagesendsthismessagetotheWindowprocedurethatisappointedtotheWindowclassthatwehaveusedtocreatethemainapplicationwindow.InourcaseitistheWndProcmethod,anditisresponsibleforreactingtotheoperatingsystemandtheapplicationevents.WhentheGetMessagemethodreturnsaresultthatislessthanzero,themessageloopstopsandtheapplicationexits.
Sothefinalstep,thatisstepfour,isthemessageprocessinginsideWndProc.Thishasfourparameters:hwndistheWindowhandleandallowsyoutointeractwiththewindow,msgisthemessageid,andwParamandlParamcontainspecificdataforeachsystemmessage.InourWindowprocedure,wehandletheWM_CLOSEandWM_DESTROYmessagesexplicitlytoshowanexampleofmessagehandling,andbydefault,wepassallmessagestoastandardmessagehandler.Ifweruntheapplication,wewillseethatitshowstheemptyapplicationwindowwiththecustomtitle.
Nowlet’saddthecodetoshowasimplebuttonclickhandlerthatwillstartanasynchronousoperation.ThiscodereplacestheWndProcdefinitionfromtheprecedingcodelisting:
constUINTIDC_START_BUTTON=101;
constUINTWM_ASYNC_TASK_COMPLETED=WM_USER+0;
DWORDWINAPISimulateAsyncOperation(LPVOIDlpHwnd)
{
//pretendingthatthisisanasyncoperation
//poststhemessagetotheUImessageloop
//fromotherthread
HWNDhwnd=*((HWND*)lpHwnd);
Sleep(10000);
SendMessage(hwnd,WM_ASYNC_TASK_COMPLETED,NULL,NULL);
return0;
}
LRESULTCALLBACKWndProc(HWNDhwnd,UINTmsg,WPARAMwParam,LPARAMlParam)
{
switch(msg)
{
caseWM_CREATE:
{
HGDIOBJhfDefault=GetStockObject(DEFAULT_GUI_FONT);
HWNDhWndButton=CreateWindowEx(NULL,
"BUTTON",
"OK",
WS_TABSTOP|WS_VISIBLE|
WS_CHILD|BS_DEFPUSHBUTTON,
50,
80,
100,
24,
hwnd,
www.EBooksWorld.ir
![Page 312: Mastering C# Concurrency - EBooksWorld...a print book customer, you are entitled to a discount on the eBook copy. Get in touch with us at for more details](https://reader035.vdocument.in/reader035/viewer/2022062917/5ed62e6504e9cb4adb670aed/html5/thumbnails/312.jpg)
(HMENU)IDC_START_BUTTON,
GetModuleHandle(NULL),
NULL);
SendMessage(hWndButton,
WM_SETFONT,
(WPARAM)hfDefault,
MAKELPARAM(FALSE,0));
}
break;
caseWM_COMMAND:
switch(LOWORD(wParam))
{
caseIDC_START_BUTTON:
{
HANDLEthreadHandle=CreateThread(NULL,0,
SimulateAsyncOperation,
&hwnd,0,NULL);
//wedonotneedthehandle,sojustcloseit
CloseHandle(threadHandle);
MessageBox(hwnd,
"Startbuttonpressed",
"Information",
MB_ICONINFORMATION);
}
break;
}
break;
caseWM_ASYNC_TASK_COMPLETED:
MessageBox(hwnd,
"Operationcompleted",
"Information",
MB_ICONINFORMATION);
break;
caseWM_CLOSE:
//sendsWM_DESTROY
DestroyWindow(hwnd);
break;
caseWM_DESTROY:
//Windowcleanuphere
PostQuitMessage(0);
break;
default:
returnDefWindowProc(hwnd,msg,wParam,lParam);
}
return0;
}
Inthebeginning,wehavecreatedidentifiersforabuttonandacustommessage.Thedetailsarenotrelevanthere;theyarejustsomenumericidentifiers.ThenextpartisanasynchronousoperationcodeinsidetheSimulateAsyncOperationmethod.Itjustblocksthecurrentthreadfor5secondsandthensendsacustommessagetotheWindowhandlethatitgetsthroughtheinputparameter.
TheremainingcodeisplacedinsidemessagehandlersintheWndProcWindowprocedure.www.EBooksWorld.ir
![Page 313: Mastering C# Concurrency - EBooksWorld...a print book customer, you are entitled to a discount on the eBook copy. Get in touch with us at for more details](https://reader035.vdocument.in/reader035/viewer/2022062917/5ed62e6504e9cb4adb670aed/html5/thumbnails/313.jpg)
ThefirstimportantplaceistheWM_CREATEmessagehandler.Herewecreatedabutton,andsetthebuttontextfonttoadefaultsystemfont.Theotherdetailsarenotimportanthere;justnoticetheuseofIDC_START_BUTTONinsidetheCreateWindowExmethodcall.Thisidentifierwillbeusedlaterinthemessagethattheoperatingsystemwillsendwhenthisbuttonisclicked.ThismessagewillbeprocessedbytheWM_COMMANDmessagehandler.Thesendingelementidentifierispassedinalow-orderwordofthewParamvalue.Inthecaseofourbuttonclick,thisvaluewillbeIDC_START_BUTTON.WecanthinkofthislikethecommonButton_Clickhandlerinhigher-levelframeworkssuchasWindowsFormsorWPF.Insidethisbuttonclickhandler,wehavecreatedaseparatethreadthatwillruntheSimulateAsyncOperationmethod.Thenthesimplestsolutionistoshowamodalmessageboxshowingthattheoperationhasbeenstarted.
Thelast,butnottheleast,stepishowweruncontinuationcodeaftertheasynchronousoperationcompletes.Theoperationsendsacustommessage,andwehandleitwiththeWM_ASYNC_TASK_COMPLETEDmessagehandler.Itsimplyshowsamessageboxinformingaboutthattheoperationhasbeencompleted.Theoperationtakes10secondstocomplete,soyoucanclosethefirstmessageboxanddragaroundtheapplicationwindowtomakesurethatitstaysresponsive.
Ofcourse,ifwerunSimulateAsyncOperationontheUIthread,itwillfreeze.Simplyreplacethebuttonclickhandlercodewiththistomakesurethisreallyhappens:
caseIDC_START_BUTTON:
{
MessageBox(hwnd,
"Startbuttonpressed",
"Information",
MB_ICONINFORMATION);
SimulateAsyncOperation(&hwnd);
}
break;
Nowifwerunthecodewiththesechanges,theapplicationwindowwillstoprespondingfor10secondsafterwepressthebuttonandclosethemodaldialog.Thisperfectlyillustrateswhatwearetryingtoachieve;doalltheworkontheotherthreads,leavetheUIthreadjusttohandlemessagesasfastaspossible,andyouwillgetagreatandresponsiveUIinyourapplication.
However,inmodernUIprogramming,theabstractionlevelisveryhigh,andusuallyyoucannotbesureifsomecoderunsontheUIthreadornotjustbylookingatit.ConsiderthisC#codethatcanbeapartofanyWPFapplication:
privatestaticasyncvoidClick(objectsender,EventArgse)
{
MessageBox.Show("Startingasynchronousoperation….");
awaitSomeOperationAsync();
MessageBox.Show("Asynchronousoperationcomplete!");
}
Thisisabuttonclickhandlerthatlogicallydoesthesamethingasthepreviouscode—www.EBooksWorld.ir
![Page 314: Mastering C# Concurrency - EBooksWorld...a print book customer, you are entitled to a discount on the eBook copy. Get in touch with us at for more details](https://reader035.vdocument.in/reader035/viewer/2022062917/5ed62e6504e9cb4adb670aed/html5/thumbnails/314.jpg)
showsadialog,runsanasynchronousoperation,andnotifiesuswithmessageboxesaboutthestartandendoftheoperation.ItismuchsimplerthanthenativeWin32applicationwindowproceduremessagehandlingcode.However,wepaythepricebynotknowingthedetails,andbyjustlookingatthispieceofcode,wecannotsayanythingaboutwhatthreadwillrunwhichpartofthiscode.
www.EBooksWorld.ir
![Page 315: Mastering C# Concurrency - EBooksWorld...a print book customer, you are entitled to a discount on the eBook copy. Get in touch with us at for more details](https://reader035.vdocument.in/reader035/viewer/2022062917/5ed62e6504e9cb4adb670aed/html5/thumbnails/315.jpg)
www.EBooksWorld.ir
![Page 316: Mastering C# Concurrency - EBooksWorld...a print book customer, you are entitled to a discount on the eBook copy. Get in touch with us at for more details](https://reader035.vdocument.in/reader035/viewer/2022062917/5ed62e6504e9cb4adb670aed/html5/thumbnails/316.jpg)
CommonproblemsandsolutionsToseewhatcanhappenifwedonotcontrolhowexactlythecodecorrelatestothreads,let’sstartwithasimpleWPFapplicationthathasthreedifferentbuttons.Inthisparticularcase,itisnotrelevanthowtheWPFapplicationgetscreatedandhowwecomposeUIcontrols,sowearegoingtoconcentrateonthecodeinsidethebuttonclickhandlers.AllthecodeforthissampleislocatedintheAsyncInUIprojectinthesamplesforChapter9.Besidesthis,wewillnotuseasyncandawaityet,becausetheywillcreateonemoreabstractionlevelandthusmakethecodehardertounderstand.
ThefirstbuttontriestocallaTaskreturningmethodsynchronously:
privatestaticvoidSyncClick(objectsender,EventArgse)
{
_label.Content=string.Empty;
try
{
stringresult=TaskMethod().Result;
_label.Content=result;
}
catch(Exceptionex)
{
_label.Content=ex.Message;
}
}
WithoutknowingexactlywhatTaskMethodis,itisimpossibletopredicthowthisprogramwillbehave.Fornow,wewillexperimentandonlythenlookatitscodeandseewhathappened.IfweruntheapplicationandclickontheStartsynchronousoperationbutton,besidesanunresponsiveUI,wewillgetaweirderrormessage:
Oneormoreerrorsoccurred
FromChapter5,C#LanguageSupportforAsynchrony,wealreadyknowthatthisisamessagefromtheAggregateExceptioninstance.TheeasiestwaytogettherealexceptionmessageisbygettingtheTaskresultthroughtheGetAwaitermethodcall.Thenewlineofcodewillbe:
stringresult=TaskMethod().GetAwaiter().GetResult();
ThistimetheUIgetsblockedagain,butwegettheactualerrormessage:
Thecallingthreadcannotaccessthisobjectbecauseadifferentthread
ownsit.
ThismessagetellsusthatwearetryingtoaccessaUIcontrolfromanon-UIthread,whichisnotallowed.NowisthetimetodigintotheTaskMethodcode:
privatestaticTask<string>TaskMethod()
{
returnTaskMethod(TaskScheduler.Default);
}
www.EBooksWorld.ir
![Page 317: Mastering C# Concurrency - EBooksWorld...a print book customer, you are entitled to a discount on the eBook copy. Get in touch with us at for more details](https://reader035.vdocument.in/reader035/viewer/2022062917/5ed62e6504e9cb4adb670aed/html5/thumbnails/317.jpg)
privatestaticTask<string>TaskMethod(TaskSchedulerscheduler)
{
Taskdelay=Task.Delay(TimeSpan.FromSeconds(5));
returndelay.ContinueWith(t=>
{
stringstr=string.Format(
"Taskisrunningonathreadid{0}.Isthreadpoolthread:{1}",
Thread.CurrentThread.ManagedThreadId,
Thread.CurrentThread.IsThreadPoolThread);
_label.Content=str;
returnstr;
},
scheduler);
}
Sowecanseethatwehavecreatedatimertaskandthensetupacontinuationtaskusingthedefaulttaskscheduler,whichtriestosetthelabeltext.Sincethedefaulttaskschedulerpoststhetaskcodetothethreadpool,wegetamultithreadedaccesserror.
Wehavealreadycoveredtaskschedulersearlierinthebook,andweknowthatwecangetoneforthecurrentsynchronizationcontext.Fornow,let’ssaythatthiswillallowustopostthecodetotheUIthread,andthiswouldresolvetheissuethatwehave.Itseemsthatifwemodifythecodetouseapropertaskscheduler,wewillgettherequiredresult:
stringresult=TaskMethod(
TaskScheduler.FromCurrentSynchronizationContext()).Result;
Unfortunately,ifwerunthemodifiedprogramandpressthebutton,theapplicationwillhang.ThereasonforthiswillbecomeclearwhenwegetbacktotheWndProcWindowproceduresourcecode.WewillmakeablockingcalltoTaskMethodfromthebuttonclickhandler,waitingfortheasynchronousoperationtocomplete.However,thebuttonclickhandlerrunsontheUIthread,sothisstopsthemessageloopfromspinningandtherefore,wewillnevergetamessagefromtheasynchronousoperationbecausethemessageloopcannotprocessthemessageasitisstopped.ItisaclassicdeadlocksituationandshowsthatusingsynchronouscallsontasksontheUIthreadisquitedangerous.
Nevertheless,wecanmakethiscodework.WPFallowsustorunthemessageloopmanually:
publicstaticclassTaskExtensions
{
publicstaticTWaitWithNestedMessageLoop<T>(thisTask<T>task)
{
varnested=newDispatcherFrame();
task.ContinueWith(_=>nested.Continue=false,TaskScheduler.Default);
Dispatcher.PushFrame(nested);
returntask.Result;
}
}
Thiscodecreatesanestedmessageloop.Thismeansthatthemainmessagelooppauses,thisonestartstoprocessmessagesuntilwestopit,andthenthemainloopgetsbackin
www.EBooksWorld.ir
![Page 318: Mastering C# Concurrency - EBooksWorld...a print book customer, you are entitled to a discount on the eBook copy. Get in touch with us at for more details](https://reader035.vdocument.in/reader035/viewer/2022062917/5ed62e6504e9cb4adb670aed/html5/thumbnails/318.jpg)
control.Sofirst,wecreatedthenestedmessageloop.Thenwesetupacontinuationtaskthatisgoingtorunonathreadpoolworkerthread.Thistaskwillstopthenestedmessageloopwhentheinitialtaskcompletes.
Finally,westartedthenestedmessageloop.ThePushFramemethodcallisblockeduntilsomeonesetstheContinuepropertyonthemessagelooptofalse.ThenestedmessageloopwillprocesssystemeventsandallowtheUItostayresponsivewhilewewaitfortheinitialtasktocomplete.Whenthiscompletes,thecontinuationtaskstopsthenestedmessageloopbysettingitsContinuepropertytofalse,andthenwewillgetthetaskresult(whichwillnotblocknow,becausethetaskhasbeencompleted)andreturnit.
Now,let’schangethecodeandrunit:
stringresult=TaskMethod(
TaskScheduler.FromCurrentSynchronizationContext())
.WaitWithNestedMessageLoop();
TheUIstaysresponsive,andwegetamessageaboutthecodethatworkswhilealabelcontrolrunsontheUIthread,whichisexactlywhatwewantedtoachieve.
Anasynchronouscode,however,willworkfine,becauseitdoesnotblocktheUIthreadandthemessageloop.Toprovethis,let’strytorunasynchronousoperationsonthethreadpoolandontheUIthread:
privatestaticvoidAsyncClick(objectsender,EventArgse)
{
_label.Content=string.Empty;
Mouse.OverrideCursor=Cursors.Wait;
Task<string>task=TaskMethod();
task.ContinueWith(t=>
{
_label.Content=t.Exception.InnerException.Message;
Mouse.OverrideCursor=null;
},
CancellationToken.None,
TaskContinuationOptions.OnlyOnFaulted,
TaskScheduler.FromCurrentSynchronizationContext()
);
}
privatestaticvoidAsyncOkClick(objectsender,EventArgse)
{
_label.Content=string.Empty;
Mouse.OverrideCursor=Cursors.Wait;
Task<string>task=TaskMethod(
TaskScheduler.FromCurrentSynchronizationContext());
task.ContinueWith(t=>Mouse.OverrideCursor=null,
CancellationToken.None,
TaskContinuationOptions.None,
TaskScheduler.FromCurrentSynchronizationContext());
}
Sincewedidnotwanttouseawaithere,wehavetosetupcontinuationtaskstooutput
www.EBooksWorld.ir
![Page 319: Mastering C# Concurrency - EBooksWorld...a print book customer, you are entitled to a discount on the eBook copy. Get in touch with us at for more details](https://reader035.vdocument.in/reader035/viewer/2022062917/5ed62e6504e9cb4adb670aed/html5/thumbnails/319.jpg)
theresult.IntheAsyncClickmethod,weknowthattheasynchronouscallisgoingtofail,sowesetupanerrorhandlingcontinuationtaskusingtheUIthreadtaskscheduler.Inthesecondcase,everythingisgoingtobefine,sothecontinuationtaskwillshowasuccessmessage.Runningtheprogramandclickingonthesecondandthirdbuttonsprovesourassumptions.
www.EBooksWorld.ir
![Page 320: Mastering C# Concurrency - EBooksWorld...a print book customer, you are entitled to a discount on the eBook copy. Get in touch with us at for more details](https://reader035.vdocument.in/reader035/viewer/2022062917/5ed62e6504e9cb4adb670aed/html5/thumbnails/320.jpg)
www.EBooksWorld.ir
![Page 321: Mastering C# Concurrency - EBooksWorld...a print book customer, you are entitled to a discount on the eBook copy. Get in touch with us at for more details](https://reader035.vdocument.in/reader035/viewer/2022062917/5ed62e6504e9cb4adb670aed/html5/thumbnails/321.jpg)
HowtheawaitkeywordworksNowlet’swriteabuttonclickhandlerusingawaitandseewhathaschanged:
privatestaticasyncvoidClick(objectsender,EventArgse)
{
_label.Content="Startingasynchronousoperation….";
awaitSomeOperationAsync();
_label.Content="Asynchronousoperationcomplete!";
}
Onceagain,withoutknowingexactlywhatSomeOperationAsyncis,itisstillimpossibletoknowhowthiscodeisgoingtobehave.Imaginethesimplestasynchronousmethodimplementation:
staticTaskSomeOperationAsync()
{
returnTask.Delay(TimeSpan.FromSeconds(5));
}
Inthiscase,theprogramwillrunsuccessfully,whichmeansthatthecontinuationcoderunsontheUIthread.Tofindouthowthishappens,weneedtoreviewtwoimportantabstractions:executionandsynchronizationcontexts.
www.EBooksWorld.ir
![Page 322: Mastering C# Concurrency - EBooksWorld...a print book customer, you are entitled to a discount on the eBook copy. Get in touch with us at for more details](https://reader035.vdocument.in/reader035/viewer/2022062917/5ed62e6504e9cb4adb670aed/html5/thumbnails/322.jpg)
ExecutionandsynchronizationcontextsAnexecutioncontextcontainsallthedatarelatedtothecurrentenvironmentinwhichathreadisrunning.Usually,thereisnoneedtousethisdirectly;itisusedbytheframeworktocontainthethread’slocalinformationsuchassecurityinformation.Whenneeded,itispossibletorestorethisinformationtoanotherthread.theC#infrastructurecapturestheexecutioncontextandflowsitintoacontinuationcodebydefault.
HereisanexampleofthecodegeneratedbytheC#compilertoperformanasynchronousmethodcallwithawait:
publicAsyncVoidMethodBuilder<>t__builder;
...
TaskAwaiterawaiter=Program.SomeOperationAsync().GetAwaiter();
...
//incasetheoperationisnotcompletedyet
this.<>__builder.AwaitUnsafeOnCompleted(refawaiter,refthis);
SoifwelookattheAsyncVoidMethodBuilder.AwaitUnsafeOnCompletedmethod,itwillcontainthefollowingcode:
[SecuritySafeCritical]
publicvoidAwaitUnsafeOnCompleted<TAwaiter,TStateMachine>(
refTAwaiterawaiter,refTStateMachinestateMachine)
whereTAwaiter:ICriticalNotifyCompletion
whereTStateMachine:IAsyncStateMachine
{
try
{
varcontinuation=m_coreState
.GetCompletionAction(refthis,refstateMachine);
Contract.Assert(continuation!=null,
"GetCompletionActionshouldalwaysreturnavalidaction.");
awaiter.UnsafeOnCompleted(continuation);
}
catch(Exceptione)
{
AsyncMethodBuilderCore.ThrowAsync(e,targetContext:null);
}
}
Now,wegetacontinuationdelegatebycallingtheGetCompletionActionmethod:
internalActionGetCompletionAction<TMethodBuilder,TStateMachine>(
refTMethodBuilderbuilder,refTStateMachinestateMachine)
whereTMethodBuilder:IAsyncMethodBuilder
whereTStateMachine:IAsyncStateMachine
{
...
//ThebuilderneedstoflowExecutionContext,socaptureit.
varcapturedContext=ExecutionContext.FastCapture();
www.EBooksWorld.ir
![Page 323: Mastering C# Concurrency - EBooksWorld...a print book customer, you are entitled to a discount on the eBook copy. Get in touch with us at for more details](https://reader035.vdocument.in/reader035/viewer/2022062917/5ed62e6504e9cb4adb670aed/html5/thumbnails/323.jpg)
...
}
So,wecapturethecurrentsynchronizationcontextanduseittorunacontinuationcode.
Synchronizationcontextisanotherconceptthatabstractsawaytheimplementationdetailsofsomeenvironmentthatisabletorunthecode.ItcanbeaWindowsFormsenvironmentthatrunsadelegatewiththehelpoftheControl.BeginInvokemethod,aWPFenvironmentthatcanrunthecodeusingtheDispatcherobject,orjustanyotherframeworkthatneedssuchanenvironmenttorunthecode.
Let’slookattheprecedingcode,specificallyattheawaiter.UnsafeOnCompleted(continuation)part.TheC#asyncinfrastructureusestheTaskAwaitertypefortheawaitervariable,whichhasthefollowingUnsafeOnCompletedmethod:
[SecurityCritical]
publicvoidUnsafeOnCompleted(Actioncontinuation)
{
TaskAwaiter.OnCompletedInternal(m_task,
continuation,
continueOnCapturedContext:true,
flowExecutionContext:false);
}
Youcanseethatwecapturedthecurrentsynchronizationcontext.However,noticethattheflowExecutionContextparameterissettofalse.Thisonlymeansthattheexecutioncontextflowhappensinanotherplaceinthecode;hereweareonlycapturingthecurrentsynchronizationcontext.
Well,nowweunderstandhowtheC#asynchronousinfrastructuremakesthecurrentexecutionandsynchronizationcontextsrunacontinuationcode.Isitpossibletochangethisbehavior?Theanswerisyes,itispossible.Tostopcapturingthecurrentsynchronizationcontext,wecanusethespecialConfigureAwaitmethodontheTaskinstance:
privatestaticasyncvoidClick(objectsender,EventArgse)
{
_label.Content="Startingasynchronousoperation….";
awaitSomeOperationAsync()
.ConfigureAwait(continueOnCapturedContext:false);
_label.Content="Asynchronousoperationcomplete!";
}
UsingtheConfigureAwaitmethodwillleadtoanotherawaitertype,ConfiguredTaskAwaiter.ThiswillbeusedbytheC#asynchronousinfrastructure.ItimplementsUnsafeOnCompletedslightlydifferently:
[SecurityCritical]
publicvoidUnsafeOnCompleted(Actioncontinuation)
{
TaskAwaiter.OnCompletedInternal(m_task,
continuation,
www.EBooksWorld.ir
![Page 324: Mastering C# Concurrency - EBooksWorld...a print book customer, you are entitled to a discount on the eBook copy. Get in touch with us at for more details](https://reader035.vdocument.in/reader035/viewer/2022062917/5ed62e6504e9cb4adb670aed/html5/thumbnails/324.jpg)
m_continueOnCapturedContext,
flowExecutionContext:false);
}
WecanseethatprovidingfalsetotheConfigureAwaitmethodwillcausethesynchronizationcontexttonotbecaptured.Ifwerunthemodifiedapplicationandpressthebutton,wewillgetamultithreadedUIcontrolaccessexception.
www.EBooksWorld.ir
![Page 325: Mastering C# Concurrency - EBooksWorld...a print book customer, you are entitled to a discount on the eBook copy. Get in touch with us at for more details](https://reader035.vdocument.in/reader035/viewer/2022062917/5ed62e6504e9cb4adb670aed/html5/thumbnails/325.jpg)
www.EBooksWorld.ir
![Page 326: Mastering C# Concurrency - EBooksWorld...a print book customer, you are entitled to a discount on the eBook copy. Get in touch with us at for more details](https://reader035.vdocument.in/reader035/viewer/2022062917/5ed62e6504e9cb4adb670aed/html5/thumbnails/326.jpg)
PerformanceissuesSofar,wehaveonlyobservedproblemsrelatedtomultithreadedaccesstotheUIcontrols.Bydefault,theC#awaitstatementwillusethecurrentsynchronizationandexecutioncontextsandpostthecontinuationcodetotheappropriateenvironment.IsthereanyusefortheConfigureAwaitmethod?Whyshouldweevertrytochangethedefaultbehavior?Toanswerthisquestion,considerthefollowingapplication.Thistimewewillreviewthewholecodeincludingtheonethatassemblestheapplication:
privatestaticLabel_label;
[STAThread]
staticvoidMain(string[]args)
{
varapp=newApplication();
varwin=newWindow();
varpanel=newStackPanel();
varbutton=newButton();
_label=newLabel();
_label.FontSize=32;
_label.Height=200;
button.Height=100;
button.FontSize=32;
button.Content="Startasynchronousoperations";
button.Click+=Click;
panel.Children.Add(_label);
panel.Children.Add(button);
win.Content=panel;
app.Run(win);
Console.ReadLine();
}
AthreadwherewecreatetheUIcontrolsmustbeaSingle-ThreadedApartmentthread,orSTA.ThistermcomesfromComponentObjectModel(COM)andisbasicallyrequiredfortheUImessagelooptobeabletointeractwithCOMcomponents.ManyOScomponents,suchassystemdialogs,usethistechnology.Tomakethingseasier,justrememberthattheUIthreadin.NETandWindowsmustbemarkedbytheSTAThreadattribute.
Then,wecreateseveralUIcontrols,composethemintheobjectmodel,andfinallyapp.Run(win)showstheapplicationwindowandstartsitsmessageloop:
asyncstaticvoidClick(objectsender,EventArgse)
{
_label.Content="Calculating…";
TimeSpanresultWithContext=awaitTest();
TimeSpanresultNoContext=awaitTestNoContext();
varsb=newStringBuilder();
sb.AppendLine(string.Format("Withthecontext:{0}",resultWithContext));
sb.AppendLine(string.Format("Withoutthecontext:{0}",
resultNoContext));
sb.AppendLine(string.Format("Ratio:{0:0.00}",
www.EBooksWorld.ir
![Page 327: Mastering C# Concurrency - EBooksWorld...a print book customer, you are entitled to a discount on the eBook copy. Get in touch with us at for more details](https://reader035.vdocument.in/reader035/viewer/2022062917/5ed62e6504e9cb4adb670aed/html5/thumbnails/327.jpg)
resultWithContext.TotalMilliseconds/
resultNoContext.TotalMilliseconds));
_label.Content=sb.ToString();
}
Thebuttonclickhandlerdoesaverysimplejob.Itrunstwoasynchronousoperations,getstheirresults,andthenoutputstheseresultstothelabelcontrolonthemainapplicationwindow.Sinceweusetheawaitstatement,wecanworkwiththeUIcontrolsfromthelattercode.
Nowtothemostimportantpartofthissample—asynchronousperformancetests:
asyncstaticTask<TimeSpan>Test()
{
constintiterationsNumber=100000;
varsw=newStopwatch();
sw.Start();
for(inti=0;i<iterationsNumber;i++)
{
vart=Task.Run(()=>{});
awaitt;
}
sw.Stop();
returnsw.Elapsed;
}
asyncstaticTask<TimeSpan>TestNoContext()
{
constintiterationsNumber=100000;
varsw=newStopwatch();
sw.Start();
for(inti=0;i<iterationsNumber;i++)
{
vart=Task.Run(()=>{});
awaitt.ConfigureAwait(continueOnCapturedContext:false);
}
sw.Stop();
returnsw.Elapsed;
}
Bothtestsdoalmostthesamething.Foralargenumberofiterations,theycreateatask,waitforitscompletion,andfinallyreturnthewholeamountoftimetakenbythetesttorun.ThesetwotestcodesaredifferentonlywithrespecttotheConfigureAwaitmethodusageinthesecondtestcode.However,thissubtledifferenceproducesahugeperformanceeffect.
Ifweruntheprogramandpressthebutton,wewillseequiteanoticeabledifferencebetweentestperformances.Onareferencemachine,thefirsttestisabouttentimesslowerthanthesecondone.However,ifyouruntheapplicationagainandthen,afterpressingthebutton,youstartresizingordraggingtheapplicationwindow,youwillnoticethatthefirsttestbecomesevenslower.Imanagedtomakeittwelvetimesslowerthanthesecondtest.
Theanswerissimple:thefirsttestusestheUIthreadtorunacontinuationcodeforeachoftheonehundredthousanditerations,thuspostingthesamenumberofmessagesonthe
www.EBooksWorld.ir
![Page 328: Mastering C# Concurrency - EBooksWorld...a print book customer, you are entitled to a discount on the eBook copy. Get in touch with us at for more details](https://reader035.vdocument.in/reader035/viewer/2022062917/5ed62e6504e9cb4adb670aed/html5/thumbnails/328.jpg)
UImessageloop.Whenweresizeordragthemainapplicationwindow,weproduceothermessagesintheUIthatmakethemessagelooprunslower,andthetestbecomessloweraswell.ThisisdefinitelynotagoodpracticeandshouldbecontrolledusingtheConfigureAwaitmethodcall.
Thesecondtestusesthethreadpoolworkerthreadstopostitscontinuationcode.Sincethethreadpoolisverywelloptimizedforsmall,short-runningtasks,wegetgoodperformancehere.
NoteIfyouwritealibrarycode,alwaysbecarefultoavoidthesynchronizationcontext.Ifyourcontinuationcodedoesnotrequirethis,alwaysuseConfigureAwaittoturnoffthesynchronizationcontextflow.
Afterrunningtheprecedingcodesnippet,wegetthefollowingoutput:
Imaginethatthefirsttestisathird-partycodeandcannotbemodified.Canwedoanythingaboutthis?PeopleoftentrytouseConfigureAwaitasinthefollowingexample:
asyncstaticvoidClick(objectsender,EventArgse)
{
_label.Content="Calculating…";
vardispatcher=Dispatcher.CurrentDispatcher;
TimeSpanresultWithContext=awaitTest().ConfigureAwait(false);
TimeSpanresultNoContext=awaitTestNoContext();
varsb=newStringBuilder();
sb.AppendLine(string.Format("Withthecontext:{0}",resultWithContext));
sb.AppendLine(string.Format("Withoutthecontext:{0}",
resultNoContext));
sb.AppendLine(string.Format("Ratio:{0:0.00}",
resultWithContext.TotalMilliseconds/
www.EBooksWorld.ir
![Page 329: Mastering C# Concurrency - EBooksWorld...a print book customer, you are entitled to a discount on the eBook copy. Get in touch with us at for more details](https://reader035.vdocument.in/reader035/viewer/2022062917/5ed62e6504e9cb4adb670aed/html5/thumbnails/329.jpg)
resultNoContext.TotalMilliseconds));
dispatcher.Invoke(()=>
{
_label.Content=sb.ToString();
});
}
HerewehadtoslightlymodifythecodetobeabletoworkwiththeUIcontrolfromthethreadpoolworkerthread.BeawarethatifweusetheDispatcher.CurrentDispatcher.Invokemethodtosetthelabeltext,thecodewillfailbecauseallthehighlightedcoderunsinacontinuationofthefirstawaitstatement,andthusrunsonthethreadpool.So,herewehavetogetadispatcherreferencebeforerunningtheasynchronouscode.
However,nothinghaschangedfortheTestmethoditself.ItstillcapturesthecurrentcontextandusestheUIthreadtorunalltheiterations.Tobeabletofixthefirsttest,wehavetoswitchthesynchronizationcontexttothethreadpoolbeforewerunthistest.Asimpleworkaroundwilllooklikethis:
asyncstaticvoidClick(objectsender,EventArgse)
{
_label.Content="Calculating…";
vardispatcher=Dispatcher.CurrentDispatcher;
awaitTask.Delay(1).ConfigureAwait(false);
TimeSpanresultWithContext=awaitTest();
TimeSpanresultNoContext=awaitTestNoContext();
varsb=newStringBuilder();
sb.AppendLine(string.Format("Withthecontext:{0}",resultWithContext));
sb.AppendLine(string.Format("Withoutthecontext:{0}",
resultNoContext));
sb.AppendLine(string.Format("Ratio:{0:0.00}",
resultWithContext.TotalMilliseconds/
resultNoContext.TotalMilliseconds));
dispatcher.Invoke(()=>
{
_label.Content=sb.ToString();
});
}
Nowthefirsttestisinsidethecontinuationcode,whichrunsonthethreadpoolworkerthread,anditusesthethreadpoolsynchronizationcontext.Ifweruntheapplication,wewillseethatbothtestsperformmoreorlessequally.
Thistrickcanbeveryusefulwhendealingwithpoorlywrittenthird-partylibraries.Unfortunately,usuallysuchproblemsareveryhardtonoticeatfirstglance,andyoufindthemaccidentallyintheprofilerwhilelookingfortherootsofsomeotherproblems.
Afterrunningtheprecedingcodesnippet,wegetthefollowingoutput:
www.EBooksWorld.ir
![Page 330: Mastering C# Concurrency - EBooksWorld...a print book customer, you are entitled to a discount on the eBook copy. Get in touch with us at for more details](https://reader035.vdocument.in/reader035/viewer/2022062917/5ed62e6504e9cb4adb670aed/html5/thumbnails/330.jpg)
www.EBooksWorld.ir
![Page 331: Mastering C# Concurrency - EBooksWorld...a print book customer, you are entitled to a discount on the eBook copy. Get in touch with us at for more details](https://reader035.vdocument.in/reader035/viewer/2022062917/5ed62e6504e9cb4adb670aed/html5/thumbnails/331.jpg)
www.EBooksWorld.ir
![Page 332: Mastering C# Concurrency - EBooksWorld...a print book customer, you are entitled to a discount on the eBook copy. Get in touch with us at for more details](https://reader035.vdocument.in/reader035/viewer/2022062917/5ed62e6504e9cb4adb670aed/html5/thumbnails/332.jpg)
SummaryInthischapter,wehaveseentheimplementationdetailsoftraditionalWindowsapplicationUIsthatareusuallyhiddenbytheprogrammingplatformandhigh-levelUIframeworks.WehavelearnedaboutwhattheUIthreadandmessageloopare,andwhytheyareveryimportanttokeeptheUIthreadrunningandnotblockingitwithlong-runningcode.ThenwelearnedaboutthecommonproblemsofasynchronyintheUI,andhowtoavoiddeadlocksandmultithreadedaccesstotheUIcontrols’exceptions.
OneofthemostimportanttopicscoveredinthischapterwastheC#asynchronousinfrastructureinternals,showinghowtheawaitstatementworks,andhowwecanimproveapplicationperformancebychoosingnottokeepthecurrentsynchronizationcontext.
Inthenextchapter,wewilllookattroubleshootingconcurrentprogramsingreaterdetail.WewillknowaboutmanyexcitingfeaturesofVisualStudioforprofilinganddebuggingparallelprogramsandfindouthowtocatchmoreerrorsinthedevelopmentstagewiththehelpofunitandfunctionaltests.
www.EBooksWorld.ir
![Page 333: Mastering C# Concurrency - EBooksWorld...a print book customer, you are entitled to a discount on the eBook copy. Get in touch with us at for more details](https://reader035.vdocument.in/reader035/viewer/2022062917/5ed62e6504e9cb4adb670aed/html5/thumbnails/333.jpg)
www.EBooksWorld.ir
![Page 334: Mastering C# Concurrency - EBooksWorld...a print book customer, you are entitled to a discount on the eBook copy. Get in touch with us at for more details](https://reader035.vdocument.in/reader035/viewer/2022062917/5ed62e6504e9cb4adb670aed/html5/thumbnails/334.jpg)
Chapter10.TroubleshootingParallelProgramsThischapterisdedicatedtoparallelprogramdebuggingspecifics.Wewillreviewhowconcurrentcodeisdifferent,whatadditionalproblemsweusuallyget,andwhatcanbedonetofindandfixbugseffectivelyinmultithreadedapplications.
www.EBooksWorld.ir
![Page 335: Mastering C# Concurrency - EBooksWorld...a print book customer, you are entitled to a discount on the eBook copy. Get in touch with us at for more details](https://reader035.vdocument.in/reader035/viewer/2022062917/5ed62e6504e9cb4adb670aed/html5/thumbnails/335.jpg)
HowtroubleshootingparallelprogramsisdifferentAconcurrentprogramlikeanyusualprogramcancontainprogrammingerrorsthatcouldleadtoincorrectresults.However,concurrencyusuallyleadsprogramstobecomemorecomplicated,causingerrorstobetrickierandhardertofind.AsmentionedinChapter1,TraditionalConcurrency,therearetypicalproblemsrelatedtoconcurrentsharedstateaccess—raceconditionsanddeadlocks,buttherearemanyotherkindsofproblemsspecifictoconcurrentprograms.Whilewewillnottrytodescribeeverykindofproblemindetail,sinceitwilltakeanotherbooktodothat,wewillinsteaddescribeseveraltechniquesthatallowustodetectandfixproblemsoverthedifferentstagesofworkingwithconcurrentprograms.
www.EBooksWorld.ir
![Page 336: Mastering C# Concurrency - EBooksWorld...a print book customer, you are entitled to a discount on the eBook copy. Get in touch with us at for more details](https://reader035.vdocument.in/reader035/viewer/2022062917/5ed62e6504e9cb4adb670aed/html5/thumbnails/336.jpg)
HeisenbugsThisisonemoreproblemtype,notstrictlyrelatedtoconcurrentprogramming,butmuchmorecommonwithit,usuallyreferredtoasheisenbug.ThistermisdefinedinWikipediaasfollows:
Incomputerprogrammingjargon,aheisenbugisasoftwarebugthatseemstodisappearoralteritsbehaviourwhenoneattemptstostudyit.ThetermisapunonthenameofWernerHeisenberg,thephysicistwhofirstassertedtheobservereffectofquantummechanics,whichstatesthattheactofobservingasysteminevitablyaltersitsstate.
Theseproblemsareusuallyextremelyhardtoreproduceanddebug,sincetheyusuallyappearinsomespecialconditionssuchashighuserload,orsomespecificeventstiming,andmore.Thisisthekindofbugwhichyouwillinevitablymeetwhiledevelopingconcurrentapplications.
Besideswhatwehavementionedsofar,concurrentprogramscanhaveproblemsrelatedtoinfrastructure,suchassynchronizationcontextsandUI,performanceproblems,oranyotherkindofproblems,whicharenotrelatedtoconcurrencyandmultithreadingatall.
Tomakeyourprogramlesserror-prone,youhavetouseacombinedapproachthatallowsthefindingandeliminationofbugsinthedifferentstagesofdevelopingyourapplicationfromwritingcodetoanalyzinglogsofproductiondeployment.Therearethreemainstagesthatarecrucialtocreaterobustandperformantapplications:
Writingtests:Thisisaveryimportantstepthatcandramaticallyreducebugsinyourcode.Withthesetests,itispossibletodetectproblemsrightafterwritingthecode,orafterdeployingyourapplicationintoatestenvironment.Debugging:VisualStudiohasspecificfeaturesandtoolstomakedebuggingconcurrentapplicationseasier.Performancemeasurementandprofiling:Thisisonemoreveryimportantstepthatcanhelptodetectwhetheryourprogramspendstoomuchtimeswitchingbetweenthreadsorblockingtheminsteadofdoingitsjob.
www.EBooksWorld.ir
![Page 337: Mastering C# Concurrency - EBooksWorld...a print book customer, you are entitled to a discount on the eBook copy. Get in touch with us at for more details](https://reader035.vdocument.in/reader035/viewer/2022062917/5ed62e6504e9cb4adb670aed/html5/thumbnails/337.jpg)
www.EBooksWorld.ir
![Page 338: Mastering C# Concurrency - EBooksWorld...a print book customer, you are entitled to a discount on the eBook copy. Get in touch with us at for more details](https://reader035.vdocument.in/reader035/viewer/2022062917/5ed62e6504e9cb4adb670aed/html5/thumbnails/338.jpg)
WritingtestsTestsallowustodetecterrorsattheveryearlystagesofdevelopment.Theyrequiresignificantinvestmentintermsoftimeandeffort,butinreturntheysavealotoftimethatcouldbelaterspentindebuggingtheapplication,whichisalwaysmuchharder.Therearedifferentkindsofteststhatcanhelptodetectdifferentproblemsintheapplication.
www.EBooksWorld.ir
![Page 339: Mastering C# Concurrency - EBooksWorld...a print book customer, you are entitled to a discount on the eBook copy. Get in touch with us at for more details](https://reader035.vdocument.in/reader035/viewer/2022062917/5ed62e6504e9cb4adb670aed/html5/thumbnails/339.jpg)
LoadtestsIfyourapplicationhastodealwithmultipleconcurrentusers,itislikelythatwiththeincreaseinthenumberofusers,youwillexperienceproblemsthatcannotberevealedinnormalconditions.Simulatingalargeuserloadandfurtherloganalysis,orstudyingprofilingresultsisalwaysagoodideaandapowerfultooltodetectpotentialpitfalls.
InChapter8,Server-SideAsynchrony,wereviewedacoupleofwaystoorganizealoadtest.Tosimulatereallylargeuseractivity,itcouldbenotenoughtouseasinglemachine.ItispossibletouseVisualStudioOnlinetorunaloadtestusingthepowerofMicrosoftAzuretorunseveralvirtualmachinesandusethemalltocreateatestloadforyourapplication.YouwillneedaVisualStudioOnlineaccount,andyouwillneedtosetaspecialflaginyourtestsettingsfile:
www.EBooksWorld.ir
![Page 340: Mastering C# Concurrency - EBooksWorld...a print book customer, you are entitled to a discount on the eBook copy. Get in touch with us at for more details](https://reader035.vdocument.in/reader035/viewer/2022062917/5ed62e6504e9cb4adb670aed/html5/thumbnails/340.jpg)
UnittestsWithunittests,wecanperformtestsonsmallisolatedpartsofourcode.Forexample,ifwehaveanAsyncCounterclassthatcontainssomeconcurrentcountercalculations.Thefirstmethodcontainsaracecondition,whichleadstoincorrectcountervaluecalculation:
publicasyncTask<int>CountWithRaceConditionAsync()
{
constintiterations=10000;
varcounter=0;
Actioncount=
()=>
{
for(inti=0;i<iterations;i++)
{
counter++;
Thread.SpinWait(100);
counter--;
}
};
vartasks=
Enumerable
.Range(0,8)
.Select(n=>Task.Run(count))
.ToArray();
awaitTask.WhenAll(tasks);
returncounter;
}
ThesecondmethodisimplementedusingtheInterlockedoperations,andthusdoesnothaveproblemswithraceconditions:
publicasyncTask<int>CountWithInterlockedAsync()
{
constintiterations=10000;
varcounter=0;
Actioncount=
()=>
{
for(inti=0;i<iterations;i++)
{
Interlocked.Increment(refcounter);
Thread.SpinWait(100);
Interlocked.Decrement(refcounter);
}
};
vartasks=
Enumerable
.Range(0,8)
.Select(n=>Task.Run(count))
.ToArray();
awaitTask.WhenAll(tasks);
www.EBooksWorld.ir
![Page 341: Mastering C# Concurrency - EBooksWorld...a print book customer, you are entitled to a discount on the eBook copy. Get in touch with us at for more details](https://reader035.vdocument.in/reader035/viewer/2022062917/5ed62e6504e9cb4adb670aed/html5/thumbnails/341.jpg)
returncounter;
}
However,thefirstmethodcansometimesproducecorrectresults,soanincorrectimplementationcanmakeitswayintoaproductioncode.Topreventthisfromhappening,let’swriteatestthatrunscalculationsandcheckstheirresults.Towritetests,wewillusethestandardVisualStudiounittestprojectandtheVisualStudioUnitTestingFramework.Thetesttocheckthesecounterslookslikethis:
[TestClass]
publicclassCounterTests
{
[TestMethod]
publicasyncTaskTestCounterWithRaceCondition()
{
varcounter=newAsyncCounter();
intcount=awaitcounter.CountWithRaceConditionAsync();
Assert.AreEqual(0,count);
}
[TestMethod]
publicasyncTaskTestCounterWitInterlocked()
{
varcounter=newAsyncCounter();
intcount=awaitcounter.CountWithInterlockedAsync();
Assert.AreEqual(0,count);
}
}
NoticethatthetestmethodsaremarkedasasyncandreturnedasTask.Thisallowsustouseawaitinsidetests,andthisissupportedinallthemajormodernunittestingframeworks.TheTestClassattributeinformstheunittestingframeworkthatthisclasscontainsunittests,andTestMethodmarksasingletest.
Toruntests,wenavigatetotheTest|Run|AllTests…menuoption.ThenyouwillseetheTestExplorerwindowthatshowstheunittestresults.Theraceconditionunittestwillfail,becauseweexpectittoreturn0,butduetotheraceconditionitusuallyreturnssomeothernumber.Theothertestwillsucceed:
www.EBooksWorld.ir
![Page 342: Mastering C# Concurrency - EBooksWorld...a print book customer, you are entitled to a discount on the eBook copy. Get in touch with us at for more details](https://reader035.vdocument.in/reader035/viewer/2022062917/5ed62e6504e9cb4adb670aed/html5/thumbnails/342.jpg)
Nowlet’strytowriteatestthatwilldetectdeadlocks.
First,wewillprepareanotherasynchronouslibrary,AsyncLib,thatcontainstwomethods.Thefirstmethodjustwaitsforonesecondandcompletessuccessfully.Thesecondonecontainsacodethatintentionallysimulatesdeadlock:
publicclassAsyncLib
{
publicasyncTaskGoodMethodAsync()
{
awaitTask.Delay(TimeSpan.FromSeconds(1));
}
publicasyncTaskDeadlockMethodAsync()
{
varlock1=newobject();
varlock2=newobject();
Tasktask1=Task.Run(()=>
{
lock(lock1)
{
Thread.Sleep(200);
lock(lock2)
{
}
}
});
Tasktask2=Task.Run(()=>
{
lock(lock2)
{
Thread.Sleep(200);
lock(lock1)
{
www.EBooksWorld.ir
![Page 343: Mastering C# Concurrency - EBooksWorld...a print book customer, you are entitled to a discount on the eBook copy. Get in touch with us at for more details](https://reader035.vdocument.in/reader035/viewer/2022062917/5ed62e6504e9cb4adb670aed/html5/thumbnails/343.jpg)
}
}
});
awaitTask.WhenAll(task1,task2);
}
}
Todetectadeadlock,wecanonlycheckwhetheranasynchronousmethodcallcompletesbeforeacertaintimeout.WecanaddanextensionmethodtoTaskthatwillhelpustosettheexpectedexecutiontimeoutvalueinmilliseconds.Afterthetimeoutexpires,wewillgetTimeoutExceptionifthetaskisnotcompleted:
publicstaticclassTaskExtensions
{
publicstaticasyncTaskTimeoutAfter(thisTasktask,
intmillisecondsTimeout)
{
if(task==awaitTask.WhenAny(task,
Task.Delay(millisecondsTimeout)))
{
awaittask;
}
else
{
thrownewTimeoutException();
}
}
}
Theunittestcodewillbeveryeasy—we’lljustaddaTimeoutAftermethodcalltoeachasynchronousfunction:
[TestClass]
publicclassLockTests
{
[TestMethod]
publicasyncTaskTestGoodAsync()
{
varlib=newAsyncLib();
awaitlib.GoodMethodAsync().TimeoutAfter(2000);
}
[TestMethod]
publicasyncTaskTestDeadlockAsync()
{
varlib=newAsyncLib();
awaitlib.DeadlockMethodAsync().TimeoutAfter(2000);
}
}
Asaresultofrunningthistest,wewillseethatwehavedetectedadeadlock:
www.EBooksWorld.ir
![Page 344: Mastering C# Concurrency - EBooksWorld...a print book customer, you are entitled to a discount on the eBook copy. Get in touch with us at for more details](https://reader035.vdocument.in/reader035/viewer/2022062917/5ed62e6504e9cb4adb670aed/html5/thumbnails/344.jpg)
VisualStudiohasanoptiontorununittestsaftereachbuild.Thiswillmakethebuildprocessslightlylonger,butwewillseethatunittestfailsaresimilartocompilationerrors.Thisisverycomfortableandhelpstoidentifyaproblemassoonaswewritethecode.TheVisualStudio2013UltimateeditionhasafeaturecalledCodeLensthatwillshowunittesterrorsrightbesidethecoderelatedtothetest:
www.EBooksWorld.ir
![Page 345: Mastering C# Concurrency - EBooksWorld...a print book customer, you are entitled to a discount on the eBook copy. Get in touch with us at for more details](https://reader035.vdocument.in/reader035/viewer/2022062917/5ed62e6504e9cb4adb670aed/html5/thumbnails/345.jpg)
www.EBooksWorld.ir
![Page 346: Mastering C# Concurrency - EBooksWorld...a print book customer, you are entitled to a discount on the eBook copy. Get in touch with us at for more details](https://reader035.vdocument.in/reader035/viewer/2022062917/5ed62e6504e9cb4adb670aed/html5/thumbnails/346.jpg)
IntegrationtestsAunittestisaverypowerfulconceptthatcanincreaseproductqualityandcanbeusedtofindmanybugsassoonastheyappearinthecode.However,whenyourapplicationbecomesmoreandmorecomplicated,testingseparatesmallcomponentsisnotenough.Manyproblemsappearwhenweusethesecomponentstogether,andwhiletwoasynchronousmethodscanrunwellseparately,theycancauseadeadlockwhilerunningsimultaneously.Thisiswhyitisveryimportanttowritehigher-leveltestsforyourapplicationthatruntheapplication’sbusinesslogicaltogether.Suchtestsarecalledintegrationtestsbecausewecheckhowtheapplicationcomponentsworktogether.
Toillustratethisapproach,wewilltakeaslightlychangedcodefromChapter8,Server-SideAsynchrony.ThisisanOWINWebAPIapplication,andwewilltestthiswithanHTTPAPIcontroller:
publicclassHomeController:ApiController
{
[HttpGet]
publicintSync()
{
varlib=newAsyncHttp();
returnlib.CountCharactersAsync(newUri("http://google.com")).Result;
}
[HttpGet]
publicasyncTask<int>Async()
{
varlib=newAsyncHttp();
returnawaitlib.CountCharactersAsync(newUri("http://google.com"));
}
}
Thiscontrollerlooksverysimple.However,inarealapplication,controllersareusuallytheplacesthatcontainapplicationlogic,andcontrolleractionscallseveralapplicationcomponentsandusetheresultstoprovidetheclientwiththedataneeded.Hereitisshownhowtowriteanintegrationtestforsuchacontroller,soyoucanusethisapproachwithyourcode.
ReferringtoChapter8,Server-SideAsynchrony,werememberthatthiscontrollerhasaproblem.Asynchronouscalltoanasynchronousmethodcouldresultinadeadlock.Solet’swriteatestthatwilllookforadeadlockhere.First,wewillneedtomodifytheTimeoutAfterextensionmethodtodealwiththeparameterizedTask<T>type.TheeasiestapproachistousethereactiveextensionsNuGetpackage.WewillneedtoreferencetheReactiveExtensions–CoreLibrarypackage.Then,wecanwritethefollowingcode:
publicstaticTask<T>TimeoutAfter<T>(thisTask<T>task,
intmillisecondsTimeout)
{
returntask.ToObservable().Timeout(
TimeSpan.FromMilliseconds(millisecondsTimeout)).ToTask();
www.EBooksWorld.ir
![Page 347: Mastering C# Concurrency - EBooksWorld...a print book customer, you are entitled to a discount on the eBook copy. Get in touch with us at for more details](https://reader035.vdocument.in/reader035/viewer/2022062917/5ed62e6504e9cb4adb670aed/html5/thumbnails/347.jpg)
}
Then,we’regoingtowritethetest.Firstofall,weneedtoreferencetheOWINWebAPINuGetpackage.Thenweneedtoaddonemorepackage,Microsoft.Owin.Testing,thathoststhewholeOWINapplicationinmemory.ThenwewillusethenewClassInitializeandClassCleanupattributestocreateatestserverandgetridofitwhenthetestscomplete:
[TestClass]
publicclassServerInMemoryTests
{
privatestaticTestServer_server;
privatestaticHttpClient_client;
[ClassInitialize]
publicstaticvoidClassInit(TestContextcontext)
{
_server=TestServer.Create<Startup>();
_client=_server.HttpClient;
}
[TestMethod]
publicasyncTaskTestSyncAction()
{
varresponse=await_client.GetAsync("/api/Home/Sync")
.TimeoutAfter(2000);
varresult=awaitresponse.Content.ReadAsAsync<int>();
Assert.IsTrue(result>0);
}
[TestMethod]
publicasyncTaskTestAsyncAction()
{
varresponse=await_client.GetAsync("/api/Home/Async")
.TimeoutAfter(2000);
varresult=awaitresponse.Content.ReadAsAsync<int>();
Assert.IsTrue(result>0);
}
[ClassCleanup]
publicstaticvoidClassCleanup()
{
_server.Dispose();
}
}
ThistestestablishestheOWINpipelineinmemoryandusesaregularHttpClientclasstosimulatehttpcallstoHomeControllerbyexpectingtogetagreaterthanzeronumber.
However,whenwerunthistest,wearegoingtofindoutthateverythingisfineandnodeadlockwillbefound:
www.EBooksWorld.ir
![Page 348: Mastering C# Concurrency - EBooksWorld...a print book customer, you are entitled to a discount on the eBook copy. Get in touch with us at for more details](https://reader035.vdocument.in/reader035/viewer/2022062917/5ed62e6504e9cb4adb670aed/html5/thumbnails/348.jpg)
ThereasonwhythereisnodeadlockhereisthatthedeadlockwasrelatedtothesynchronizationcontextintheASP.NETenvironment,andthistestusedin-memoryhosting.However,wecandetecthereanyapplicationcomponent’sinteractionissues,andthiskindoftestisalsogoodtorunaftereachbuildinVisualStudio.
Todetectinfrastructureissues,wehavetotesttheapplicationbyrunningitinthesameenvironmentthatwillbeusedinproduction.Fortunately,thisisquiteeasytodo.Insteadofcreatinganin-memoryhost,wejustneedtorunourapplicationandslightlymodifythetestcodetousearealhttpinteraction:
[TestClass]publicclassServerHttpTests
{
privatestaticHttpClient_client;
[ClassInitialize]
publicstaticvoidClassInit(TestContextcontext)
{
_client=newHttpClient();
_client.BaseAddress=newUri("http://localhost:1845/");
}
[TestMethod]
publicasyncTaskTestSyncAction()
{
varresponse=await_client.GetAsync("/api/Home/Sync")
.TimeoutAfter(2000);
varresult=awaitresponse.Content.ReadAsAsync<int>();
Assert.IsTrue(result>0);
}
[TestMethod]
publicasyncTaskTestAsyncAction()
{
varresponse=await_client.GetAsync("/api/Home/Async")
.TimeoutAfter(2000);
www.EBooksWorld.ir
![Page 349: Mastering C# Concurrency - EBooksWorld...a print book customer, you are entitled to a discount on the eBook copy. Get in touch with us at for more details](https://reader035.vdocument.in/reader035/viewer/2022062917/5ed62e6504e9cb4adb670aed/html5/thumbnails/349.jpg)
varresult=awaitresponse.Content.ReadAsAsync<int>();
Assert.IsTrue(result>0);
}
[ClassCleanup]
publicstaticvoidClassCleanup()
{
_client.Dispose();
}
}
Noticethatthetestcoderemainsthesame.WehaveonlychangedtheHttpClientinstance.HerewejustpointittoourapplicationURL,andthisisallthatwehavechanged.Nowthetestdetectsadeadlockwhereweexpectedittooccur:
Thiskindoftestisnotintendedtoberunalongwiththebuilt-inVisualStudio.Theproperplacetorunthesetestsisyourcontinuousintegrationprocess,whenyoucreateanewbuildonyourbuildserver,deploytheapplicationintoatestenvironment,configureitandpre-populatedatastoragewithsometestdata,andthenrunatestsuiteonthisapplicationinstance.
TipThetestingstageisveryimportant,becauseitismuchhardertofindproblemsinthedebuggingorprofilingstage.Investingintestscanhelptosavealotoffurthereffortstofindoutwhatiswrongwiththeapplication,andgreatlyreducethenumberofproblemsthatgetintotheproductionenvironment.
www.EBooksWorld.ir
![Page 350: Mastering C# Concurrency - EBooksWorld...a print book customer, you are entitled to a discount on the eBook copy. Get in touch with us at for more details](https://reader035.vdocument.in/reader035/viewer/2022062917/5ed62e6504e9cb4adb670aed/html5/thumbnails/350.jpg)
www.EBooksWorld.ir
![Page 351: Mastering C# Concurrency - EBooksWorld...a print book customer, you are entitled to a discount on the eBook copy. Get in touch with us at for more details](https://reader035.vdocument.in/reader035/viewer/2022062917/5ed62e6504e9cb4adb670aed/html5/thumbnails/351.jpg)
DebuggingDebuggingasaveryextensivetopicandthereareseveralbooksaboutdebugging.NETapplicationstechniques.HerewewillreviewhowwecanstartdebuggingwithVisualStudio,andwhattoolscanhelpustodebugconcurrentapplications.
www.EBooksWorld.ir
![Page 352: Mastering C# Concurrency - EBooksWorld...a print book customer, you are entitled to a discount on the eBook copy. Get in touch with us at for more details](https://reader035.vdocument.in/reader035/viewer/2022062917/5ed62e6504e9cb4adb670aed/html5/thumbnails/352.jpg)
JustmycodesettingThereisaveryimportantsettinglocatedintheDebug,OptionsandSettings…menucalledEnableJustMyCode:
Whenthissettingisenabled,VisualStudiotriestohideadditionalinformationsuchascompiler-generatedcodeanddoesnotshowthisindebuggingwindows,concentratingonlyontheinformationrelatedtoyourcode.Thisseemscomfortable,butdonotforgetthatyoucanalwaysturnitoffandstudythewholepictureincaseyouneedtodigintotheinfrastructurecode.
www.EBooksWorld.ir
![Page 353: Mastering C# Concurrency - EBooksWorld...a print book customer, you are entitled to a discount on the eBook copy. Get in touch with us at for more details](https://reader035.vdocument.in/reader035/viewer/2022062917/5ed62e6504e9cb4adb670aed/html5/thumbnails/353.jpg)
CallstackwindowOneoftheeasiestdebuggingtoolsinVisualStudioisthecallstackwindow.Anasynchronousmethodcallusuallyconsistsoftwoparts—beginandendoperation.Ifyouhaveabreakpointinsideanasynchronousmethodbody,itisnoteasytofindoutwherethisoperationhasbeeninitiated.Fortunately,ifyouhavethelatestVisualStudio2013installedatleastonWindows8.1orWindows2012R2,thecallstackwindowwillshowyouafullcallstackincludingtheasynchronousoperationstartingpoint.
Wemayrunthiscodeunderthedebugger,asfollows:
classProgram
{
staticvoidMain(string[]args)
{
StartAsyncOperation().GetAwaiter().GetResult();
}
publicstaticasyncTaskStartAsyncOperation()
{
Console.WriteLine("Startingasyncoperation");
awaitAsyncOperation();
Console.WriteLine("Afterfinishingasyncoperation");
}
publicstaticasyncTaskAsyncOperation()
{
Console.WriteLine("Insideasyncoperation");
awaitTask.Delay(TimeSpan.FromSeconds(1));
Console.WriteLine("Asyncoperationcomplete!");
}
}
Inthiscase,wewillseeinthecallstackwindowthattheoperationhasbeeninitiatedintheStartAsyncOperationmethod:
www.EBooksWorld.ir
![Page 354: Mastering C# Concurrency - EBooksWorld...a print book customer, you are entitled to a discount on the eBook copy. Get in touch with us at for more details](https://reader035.vdocument.in/reader035/viewer/2022062917/5ed62e6504e9cb4adb670aed/html5/thumbnails/354.jpg)
www.EBooksWorld.ir
![Page 355: Mastering C# Concurrency - EBooksWorld...a print book customer, you are entitled to a discount on the eBook copy. Get in touch with us at for more details](https://reader035.vdocument.in/reader035/viewer/2022062917/5ed62e6504e9cb4adb670aed/html5/thumbnails/355.jpg)
ThreadswindowAnotherusefulVisualStudiodebuggingfeatureistheThreadswindow.Itshowsthecurrentthreadsintheapplicationandallowsustosuspendandresumeanythreadwithcorrespondingbuttonsandfilterthreadsbymarkingthemwithflagsandpressingthedoubleflagbutton:
www.EBooksWorld.ir
![Page 356: Mastering C# Concurrency - EBooksWorld...a print book customer, you are entitled to a discount on the eBook copy. Get in touch with us at for more details](https://reader035.vdocument.in/reader035/viewer/2022062917/5ed62e6504e9cb4adb670aed/html5/thumbnails/356.jpg)
TaskswindowWiththeTaskswindow,itispossibletoreviewincompleteTPLtasksandseethedifferentinformationaboutthem:
TheTaskswindowhasdeadlockdiagnosticsthatinformusabouttasksthataredeadlocked.Toseeitinaction,wehavetorunthefollowingcodeuntiladeadlockoccursandthenpressthebreakbuttononthedebuggertoolbar:
classProgram
{
staticvoidMain(string[]args)
{
DeadlockMethodAsync().GetAwaiter().GetResult();
}
publicstaticasyncTaskDeadlockMethodAsync()
{
varlock1=newobject();
varlock2=newobject();
Tasktask1=Task.Run(()=>
{
lock(lock1)
{
Thread.Sleep(200);
www.EBooksWorld.ir
![Page 357: Mastering C# Concurrency - EBooksWorld...a print book customer, you are entitled to a discount on the eBook copy. Get in touch with us at for more details](https://reader035.vdocument.in/reader035/viewer/2022062917/5ed62e6504e9cb4adb670aed/html5/thumbnails/357.jpg)
lock(lock2)
{
}
}
});
Tasktask2=Task.Run(()=>
{
lock(lock2)
{
Thread.Sleep(200);
Debugger.Break();
lock(lock1)
{
}
}
});
Debugger.Break();
//hereyoucanopenTaskswindowinVisualStudio
awaitTask.WhenAll(task1,task2);
}
}
www.EBooksWorld.ir
![Page 358: Mastering C# Concurrency - EBooksWorld...a print book customer, you are entitled to a discount on the eBook copy. Get in touch with us at for more details](https://reader035.vdocument.in/reader035/viewer/2022062917/5ed62e6504e9cb4adb670aed/html5/thumbnails/358.jpg)
ParallelstackswindowTovisualizeanasynchronousprogramflow,wecanusetheParallelStackswindow.Let’srunasimpleparallelforeachloop:
classProgram
{
staticvoidMain(string[]args)
{
ParallelForEach().GetAwaiter().GetResult();
}
publicstaticasyncTaskParallelForEach()
{
Parallel.ForEach(Enumerable.Range(0,32),i=>
{
Console.WriteLine(i);
if(i==24)
{
Debugger.Break();
}
Thread.Sleep(newRandom(i).Next(100,500));
});
}
}
ThisscreenshothasbeenmadeonavirtualmachinewithsixcoreCPUs.Weseethatoneofthetaskswasscheduledtorunonthemainthread:
www.EBooksWorld.ir
![Page 359: Mastering C# Concurrency - EBooksWorld...a print book customer, you are entitled to a discount on the eBook copy. Get in touch with us at for more details](https://reader035.vdocument.in/reader035/viewer/2022062917/5ed62e6504e9cb4adb670aed/html5/thumbnails/359.jpg)
IfweturnofftheEnableJustMyCodesetting,wewillseemoredetailsabouthowtheconcurrentprogramisorganized.
www.EBooksWorld.ir
![Page 360: Mastering C# Concurrency - EBooksWorld...a print book customer, you are entitled to a discount on the eBook copy. Get in touch with us at for more details](https://reader035.vdocument.in/reader035/viewer/2022062917/5ed62e6504e9cb4adb670aed/html5/thumbnails/360.jpg)
www.EBooksWorld.ir
![Page 361: Mastering C# Concurrency - EBooksWorld...a print book customer, you are entitled to a discount on the eBook copy. Get in touch with us at for more details](https://reader035.vdocument.in/reader035/viewer/2022062917/5ed62e6504e9cb4adb670aed/html5/thumbnails/361.jpg)
PerformancemeasurementandprofilingThereisaprofilerinVisualStudiothatcanbeusedtovisualizeconcurrencyinyourapplicationandseewhatisgoingon.DependingontheVisualStudioversion,itsbehaviorisdifferent.InVisualStudio2010,youwouldjustrunaprofilersessioncollectingconcurrencydataandgettherequiredresult.InVisualStudio2012,therewasaseparatemenuoptioncalledConcurrencyVisualizerandthisisthemostcomfortablewaytolookatconcurrencyprocessesinyourapplication.
InVisualStudio2013,thereisnoConcurrencyVisualizeroptionbydefault,andyoucanstillusetheregularprofilertocollectthebasicconcurrencyinformation.However,youcaninstallConcurrencyVisualizerseparately.
www.EBooksWorld.ir
![Page 362: Mastering C# Concurrency - EBooksWorld...a print book customer, you are entitled to a discount on the eBook copy. Get in touch with us at for more details](https://reader035.vdocument.in/reader035/viewer/2022062917/5ed62e6504e9cb4adb670aed/html5/thumbnails/362.jpg)
TheConcurrencyVisualizerTheConcurrencyVisualizerisavailableforVisualStudio2013asaseparateextension.YoucaninstallitinVisualStudio:
Aftertheinstallation,youwillgetaConcurrencyVisualizermenuoptionundertheAnalyzemenuinVisualStudio:
ConcurrencyVisualizerprovidesalotofusefulinformation.Toillustratethis,let’scomparetheparallelismgranularitytestfromChapter3,UnderstandingParallelismGranularity,andI/OthreadsfromChapter8,Server-SideAsynchrony.ThefirstprogramunderConcurrencyVisualizerwilllooklikethis:
www.EBooksWorld.ir
![Page 363: Mastering C# Concurrency - EBooksWorld...a print book customer, you are entitled to a discount on the eBook copy. Get in touch with us at for more details](https://reader035.vdocument.in/reader035/viewer/2022062917/5ed62e6504e9cb4adb670aed/html5/thumbnails/363.jpg)
ItcanbeclearlyseenthattheprogramconsumesalotofCPUresources.NowifwevisualizeI/Othreads,wewillseethatitconsumesalmostnoCPUresources:
WecanseemoredetailsifwegototheThreadstabinsidethereport.ThegranularityprogramshowsasignificantCPUload,asshownhere:
www.EBooksWorld.ir
![Page 364: Mastering C# Concurrency - EBooksWorld...a print book customer, you are entitled to a discount on the eBook copy. Get in touch with us at for more details](https://reader035.vdocument.in/reader035/viewer/2022062917/5ed62e6504e9cb4adb670aed/html5/thumbnails/364.jpg)
However,theI/Othreadsreportindicatesthatabouthalfoftheprogramtimethreadsareintheblockedstate:
Thereisalotofdatainthisreport,andifyourunaprofilersessiontocollectconcurrencyinformation,youcangetevenmoredetails.Butthisisjustthestartingpointwhereyoucanseewhatisgoingonintheprogram,anddependingontheinformationreceivedinthereport,youcanfurtherinvestigatewhytheprogramdoesnotusealltheCPUcomputationalpower,orwhyitspendsalotoftimeinsynchronizationprocess.
www.EBooksWorld.ir
![Page 365: Mastering C# Concurrency - EBooksWorld...a print book customer, you are entitled to a discount on the eBook copy. Get in touch with us at for more details](https://reader035.vdocument.in/reader035/viewer/2022062917/5ed62e6504e9cb4adb670aed/html5/thumbnails/365.jpg)
www.EBooksWorld.ir
![Page 366: Mastering C# Concurrency - EBooksWorld...a print book customer, you are entitled to a discount on the eBook copy. Get in touch with us at for more details](https://reader035.vdocument.in/reader035/viewer/2022062917/5ed62e6504e9cb4adb670aed/html5/thumbnails/366.jpg)
SummaryInthischapter,welearnedaboutthedifferentstagesofapplicationdevelopmentwherewecandetectandfixproblemsinconcurrentapplications.Werevieweddifferenttestingtechniquesthathelptopreventbugsfromgettingintotheapplicationcode.Welearnedtouseasynchronousunittests,hostanOWINWebAPIapplicationinmemory,testHTTPAPIcontrollers,andalsoadapttheseteststorunontherealhttpapplicationhostedonawebserverinatestenvironment.
WehaverevieweddifferentdebuggingtoolsincludedinVisualStudio.Thesetoolshelpustovisualizetheconcurrentprogramworkflow,showinformationaboutcurrentlyrunningTPLtasks,detectdeadlocks,allowustopauseandresumethreads,seethedetailsofeachthread,andhelpustouseasynchronouscallstacksinacomfortableway,soitisclearwherethecurrentasynchronousoperationhasbeenstarted.
WealsoinstalledaConcurrencyVisualizerextensioninVisualStudio2013andusedittofindoutwhatisgoingonintheconcurrentapplication,howmuchtimetheapplicationspendssynchronizing,blockingthreads,anddoingCPU-boundwork.
www.EBooksWorld.ir
![Page 367: Mastering C# Concurrency - EBooksWorld...a print book customer, you are entitled to a discount on the eBook copy. Get in touch with us at for more details](https://reader035.vdocument.in/reader035/viewer/2022062917/5ed62e6504e9cb4adb670aed/html5/thumbnails/367.jpg)
IndexA
ABAproblemabout/TheABAproblem
acquirefenceabout/Memorymodelandcompileroptimizations
async/Unittestsasync/awaitinfrastructure
about/Concurrentidiomsasync/awaitstatements/AsynchronousProgrammingModelasynchronousI/O
about/DeepdiveintoasynchronousI/Oasynchronouspatterns
about/AsynchronouspatternsAsynchronousProgrammingModel(APM)/AsynchronousProgrammingModelEvent-basedAsynchronousPattern(EAP)/Event-basedAsynchronousPatternTask-basedAsynchronousPattern(TAP)/Task-basedAsynchronousPattern
AsynchronousProgrammingModel(APM)about/AsynchronousProgrammingModelfeatures/AsynchronousProgrammingModel
asynchrony,forUIimportance/TheimportanceofasynchronyforUI
asynckeywordabout/Istheasynckeywordreallyneeded?
asyncoversync/RealandfakeasynchronousI/Ooperationsatomic
about/TheSystem.Threading.Interlockedclassawait/Unittestsawaitingtaskcompletion
about/Awaitingtaskcompletionawaitkeyword
working/Howtheawaitkeywordworksawaitstatement/Settingatasktimeout
www.EBooksWorld.ir
![Page 368: Mastering C# Concurrency - EBooksWorld...a print book customer, you are entitled to a discount on the eBook copy. Get in touch with us at for more details](https://reader035.vdocument.in/reader035/viewer/2022062917/5ed62e6504e9cb4adb670aed/html5/thumbnails/368.jpg)
BBing
downloadingofimages,implementingfrom/ImplementingthedownloadingofimagesfromBingURL/ImplementingthedownloadingofimagesfromBing
blockingqueue/CustomProducer/Consumerpatternimplementationboundedqueue/CustomProducer/Consumerpatternimplementation
www.EBooksWorld.ir
![Page 369: Mastering C# Concurrency - EBooksWorld...a print book customer, you are entitled to a discount on the eBook copy. Get in touch with us at for more details](https://reader035.vdocument.in/reader035/viewer/2022062917/5ed62e6504e9cb4adb670aed/html5/thumbnails/369.jpg)
CC#5.0built-insupport,forasynchrony
code,enhancingwith/EnhancingthecodewithC#5.0built-insupportforasynchrony
C#asynchronousinfrastructuresimulating,withiterators/SimulatingC#asynchronousinfrastructurewithiterators
cacheimplementing,withReaderWriterLockSlim/ImplementingacachewithReaderWriterLockSlim
cacheasidepattern/ImplementingacachewithReaderWriterLockSlimcallbacks
used,fortaskcancellation/Cancellationusingcallbacksclassconstraint
about/Thelock-freestackcoarse-grainedapproach
about/Understandinggranularityselecting/Choosingthecoarse-grainedorfine-grainedapproach
coarse-grainedapproach,withTPLabout/Latencyandthecoarse-grainedapproachwithTPL
coarse-grainedlocking/Concurrentcollectionsin.NETcodecoupling
about/Taskcompositioncommonproblems
about/Commonproblemsandsolutionssolutions/Commonproblemsandsolutions
compare-and-swap(CAS)/Implementationdetailscompareandswap(CAS)
about/TheABAproblemcompileroptimizations
about/MemorymodelandcompileroptimizationsComponentObjectModel(COM)/PerformanceissuesConcurrencyVisualizer
about/TheConcurrencyVisualizerConcurrentBag<T>
about/ConcurrentBag<T>using/ConcurrentBaginpractice
ConcurrentDictionaryabout/ConcurrentDictionaryLazy<T>/UsingLazy<T>details,implementing/Implementationdetailsimplementationdetails,using/Usingtheimplementationdetailsinpractice
ConcurrentDictionaryclasslock-freeoperations/Implementationdetails,Lock-freeoperationsfine-grainedlockoperations/Implementationdetails,Fine-grainedlock
www.EBooksWorld.ir
![Page 370: Mastering C# Concurrency - EBooksWorld...a print book customer, you are entitled to a discount on the eBook copy. Get in touch with us at for more details](https://reader035.vdocument.in/reader035/viewer/2022062917/5ed62e6504e9cb4adb670aed/html5/thumbnails/370.jpg)
operationsexclusivelockoperations/Implementationdetails,Exclusivelockoperations
concurrentidiomsabout/ConcurrentidiomsProcessTasks,inCompletionOrder/ProcessTasksinCompletionOrderparallelismdegree,limiting/Limitingtheparallelismdegreetasktimeout,setting/Settingatasktimeout
concurrentpatternsabout/Concurrentpatterns
ConcurrentQueue<T>about/ConcurrentQueue<T>
ConcurrentStack<T>about/ConcurrentStack<T>
continuationtaskabout/Taskcomposition
CPU-boundtasksandqueuesabout/CPU-boundtasksandqueues
customawaitabletypeimplementing/Implementingacustomawaitabletype
CustomProviderclass/ImplementingacachewithReaderWriterLockSlim
www.EBooksWorld.ir
![Page 371: Mastering C# Concurrency - EBooksWorld...a print book customer, you are entitled to a discount on the eBook copy. Get in touch with us at for more details](https://reader035.vdocument.in/reader035/viewer/2022062917/5ed62e6504e9cb4adb670aed/html5/thumbnails/371.jpg)
Ddeadlock
about/What’stheproblem?debugging
about/DebuggingEnableJustMyCodesetting/Justmycodesettingcallstackwindow/Callstackwindowthreadswindow/ThreadswindowTaskswindow/Taskswindowparallelstackswindow/Parallelstackswindow
doublecheckedlockingpatternabout/Thelock-freequeue
downloadingofimages,implementingfromBingabout/ImplementingthedownloadingofimagesfromBingsimplesynchronoussolution,creating/Creatingasimplesynchronoussolutionparallelsolution,creatingwithTaskParallelLibrary/CreatingaparallelsolutionwithTaskParallelLibrarycode,enhancingwithC#5.0built-insupport/EnhancingthecodewithC#5.0built-insupportforasynchronyC#asynchronousinfrastructure,simulatingwithiterators/SimulatingC#asynchronousinfrastructurewithiterators
www.EBooksWorld.ir
![Page 372: Mastering C# Concurrency - EBooksWorld...a print book customer, you are entitled to a discount on the eBook copy. Get in touch with us at for more details](https://reader035.vdocument.in/reader035/viewer/2022062917/5ed62e6504e9cb4adb670aed/html5/thumbnails/372.jpg)
EEditBin.exetool/ThenumberofthreadsEnqueuemethod/ConcurrentQueue<T>Event-basedAsynchronousPattern(EAP)
about/Event-basedAsynchronousPatternfeatures/Event-basedAsynchronousPattern
exceptionhandlingabout/Exceptionhandling
exclusivelockoperations/Exclusivelockoperationsexecutioncontext/Executionandsynchronizationcontexts
www.EBooksWorld.ir
![Page 373: Mastering C# Concurrency - EBooksWorld...a print book customer, you are entitled to a discount on the eBook copy. Get in touch with us at for more details](https://reader035.vdocument.in/reader035/viewer/2022062917/5ed62e6504e9cb4adb670aed/html5/thumbnails/373.jpg)
FfakeasynchronousI/Ooperations
about/RealandfakeasynchronousI/Ooperationsfeatures,AsynchronousProgrammingModel(APM)
low-levelpattern/AsynchronousProgrammingModellowperformanceoverhead/AsynchronousProgrammingModelcomplicatedimplementation/AsynchronousProgrammingModelcoupling,betweenasynchronousoperationproviderandconsumer/AsynchronousProgrammingModel
features,Event-basedAsynchronousPattern(EAP)high-levelpattern/Event-basedAsynchronousPatternhighoverhead/Event-basedAsynchronousPatternintendedforUIcomponents/Event-basedAsynchronousPatterncomplicatedimplementation/Event-basedAsynchronousPatterncouping,betweenasynchronousoperationproviderandconsumers/Event-basedAsynchronousPattern
features,Task-basedAsynchronousPattern(TAP)lowoverhead/Task-basedAsynchronousPatternhigh-level/Task-basedAsynchronousPatterncomfortabletouse/Task-basedAsynchronousPatternlanguagesupport,inC#/VB/Task-basedAsynchronousPatternTaskandTask<T>arefirst-classobjects/Task-basedAsynchronousPatternavoidance,ofsideeffects/Task-basedAsynchronousPattern
fine-grainedapproachabout/Understandinggranularityselecting/Choosingthecoarse-grainedorfine-grainedapproach
fine-grainedlockoperations/Fine-grainedlockoperationsfire-and-forgettasks
about/Fire-and-forgettasksforeachloop/ProcessTasksinCompletionOrderfork/joinpattern
about/Concurrentpatternsfuture
about/Taskcomposition
www.EBooksWorld.ir
![Page 374: Mastering C# Concurrency - EBooksWorld...a print book customer, you are entitled to a discount on the eBook copy. Get in touch with us at for more details](https://reader035.vdocument.in/reader035/viewer/2022062917/5ed62e6504e9cb4adb670aed/html5/thumbnails/374.jpg)
G4-gigabytetuning/Thenumberofthreadsgranularity
about/Understandinggranularity
www.EBooksWorld.ir
![Page 375: Mastering C# Concurrency - EBooksWorld...a print book customer, you are entitled to a discount on the eBook copy. Get in touch with us at for more details](https://reader035.vdocument.in/reader035/viewer/2022062917/5ed62e6504e9cb4adb670aed/html5/thumbnails/375.jpg)
Hheisenbugs
about/Heisenbugshighcoupling
about/TaskcompositionHyper-Threadingtechnology/Thenumberofthreadshyperthreadingtechnology/TheimportanceofasynchronyforUI
www.EBooksWorld.ir
![Page 376: Mastering C# Concurrency - EBooksWorld...a print book customer, you are entitled to a discount on the eBook copy. Get in touch with us at for more details](https://reader035.vdocument.in/reader035/viewer/2022062917/5ed62e6504e9cb4adb670aed/html5/thumbnails/376.jpg)
II/OandCPU-boundtasks/I/OandCPU-boundtasksI/OCompletionPort(IOCP)/I/OandCPU-boundtasksinput/outputthreads/Usingthethreadpoolintegrationtests
about/Integrationtestsinterlockedinternals
working/Interlockedinternals
www.EBooksWorld.ir
![Page 377: Mastering C# Concurrency - EBooksWorld...a print book customer, you are entitled to a discount on the eBook copy. Get in touch with us at for more details](https://reader035.vdocument.in/reader035/viewer/2022062917/5ed62e6504e9cb4adb670aed/html5/thumbnails/377.jpg)
Llatencyapproach,withTPL
about/Latencyandthecoarse-grainedapproachwithTPLlegacycodesupportscenario/Event-basedAsynchronousPatternloadtesting/Loadtestingandscalabilitylock-freecode
writing/Writinglock-freecodelock-freeoperations/Lock-freeoperationslock-freequeue
about/Thelock-freequeuelock-freestack
about/Thelock-freestacklocklocalization
about/Locklocalizationlocks
using/Usinglockslockstatement
about/Lockstatement/Standardcollectionsandsynchronizationprimitiveslowcoupling
about/Taskcomposition
www.EBooksWorld.ir
![Page 378: Mastering C# Concurrency - EBooksWorld...a print book customer, you are entitled to a discount on the eBook copy. Get in touch with us at for more details](https://reader035.vdocument.in/reader035/viewer/2022062917/5ed62e6504e9cb4adb670aed/html5/thumbnails/378.jpg)
Mmemorybarrier
about/System.Threading.SpinLock,Memorymodelandcompileroptimizationsmemorymodel
about/Memorymodelandcompileroptimizationsmessageloop/UIthreadsandmessageloopsmessagepump/UIthreadsandmessageloopsMonitorclass
about/Monitorclassmutexsynchronizationprimitive
about/Thelock-freestack
www.EBooksWorld.ir
![Page 379: Mastering C# Concurrency - EBooksWorld...a print book customer, you are entitled to a discount on the eBook copy. Get in touch with us at for more details](https://reader035.vdocument.in/reader035/viewer/2022062917/5ed62e6504e9cb4adb670aed/html5/thumbnails/379.jpg)
N.NET
concurrentcollections/Concurrentcollectionsin.NET.NET4.0+
Producer/Consumerpattern/TheProducer/Consumerpatternin.NET4.0+
www.EBooksWorld.ir
![Page 380: Mastering C# Concurrency - EBooksWorld...a print book customer, you are entitled to a discount on the eBook copy. Get in touch with us at for more details](https://reader035.vdocument.in/reader035/viewer/2022062917/5ed62e6504e9cb4adb670aed/html5/thumbnails/380.jpg)
Oonlyforlegacycodesupportscenario/AsynchronousProgrammingModeloptimizationstrategy
about/Optimizationstrategylocklocalization/Locklocalizationshareddataminimization/Shareddataminimization
OSwaitobjectsusing,withWaitHandle/UsingOSwaitobjectswithWaitHandle
OWINWebAPIframeworkabout/TheOWINWebAPIframework
www.EBooksWorld.ir
![Page 381: Mastering C# Concurrency - EBooksWorld...a print book customer, you are entitled to a discount on the eBook copy. Get in touch with us at for more details](https://reader035.vdocument.in/reader035/viewer/2022062917/5ed62e6504e9cb4adb670aed/html5/thumbnails/381.jpg)
PParallelclass
using/UsingtheParallelclassParallel.Invokemethod/Parallel.InvokeParallel.Formethod/Parallel.ForandParallel.ForeachParallel.Foreachmethod/Parallel.ForandParallel.Foreach
parallelpipelineabout/Concurrentpatternsimplementing/Parallelpipelines
parallelprogramstroubleshooting/Howtroubleshootingparallelprogramsisdifferentheisenbugs/Heisenbugs
parallelsolutioncreating,withTaskParallelLibrary/CreatingaparallelsolutionwithTaskParallelLibrary
performanceissues/Performanceissuesperformancemeasurement
about/PerformancemeasurementandprofilingPLINQ/Concurrentcollectionsin.NETproducer/consumerpattern
about/ConcurrentpatternsProducer/Consumerpattern
about/TheProducer/Consumerpatternimplementing/CustomProducer/Consumerpatternimplementationin.NET4.0+/TheProducer/Consumerpatternin.NET4.0+
profilingabout/Performancemeasurementandprofiling
promiseabout/Taskcomposition
www.EBooksWorld.ir
![Page 382: Mastering C# Concurrency - EBooksWorld...a print book customer, you are entitled to a discount on the eBook copy. Get in touch with us at for more details](https://reader035.vdocument.in/reader035/viewer/2022062917/5ed62e6504e9cb4adb670aed/html5/thumbnails/382.jpg)
Rracecondition
about/What’stheproblem?sample/What’stheproblem?
/Unittestsreaderwriterlock
about/Reader-writerlockReaderWriterLockSlim
used,forimplementingcache/ImplementingacachewithReaderWriterLockSlim
readyqueueabout/Monitorclass
realasynchronousI/Ooperationsabout/RealandfakeasynchronousI/Ooperations
releasefenceabout/Memorymodelandcompileroptimizations
replicabletask/Parallel.Invokerobustandperformantapplications
creating,stages/HeisenbugsRunLongRunningOperationmethod/ConcurrentDictionary
www.EBooksWorld.ir
![Page 383: Mastering C# Concurrency - EBooksWorld...a print book customer, you are entitled to a discount on the eBook copy. Get in touch with us at for more details](https://reader035.vdocument.in/reader035/viewer/2022062917/5ed62e6504e9cb4adb670aed/html5/thumbnails/383.jpg)
Sscalability/Serverapplications,Loadtestingandscalabilityserverapplication
about/Serverapplicationstypes/Serverapplicationsscalability/Serverapplicationsscalevertically/Serverapplicationshorizontalscalability/Serverapplications
shareddataminimizationabout/Shareddataminimization
simplesynchronoussolutioncreating/Creatingasimplesynchronoussolution
Single-ThreadedApartment(STA)/Performanceissuesspinlock
about/SpinlockThread.SpinWait/Thread.SpinWaitSystem.Threading.SpinWait/System.Threading.SpinWaitSystem.Threading.SpinLock/System.Threading.SpinLock
standardcollections/Standardcollectionsandsynchronizationprimitivessynchronizationcontext
about/Synchronizationcontextsynchronizationcontexts/Executionandsynchronizationcontextssynchronizationprimitives/StandardcollectionsandsynchronizationprimitivesSystem.Threading.Interlockedclass
about/TheSystem.Threading.InterlockedclassSystem.Threading.SpinLock
about/System.Threading.SpinLockSystem.Threading.SpinWait
about/System.Threading.SpinWaitSystem.Threading.Taskclass/Understandinggranularity
www.EBooksWorld.ir
![Page 384: Mastering C# Concurrency - EBooksWorld...a print book customer, you are entitled to a discount on the eBook copy. Get in touch with us at for more details](https://reader035.vdocument.in/reader035/viewer/2022062917/5ed62e6504e9cb4adb670aed/html5/thumbnails/384.jpg)
TTablesclass
m_buckets/Implementationdetailsm_locks/Implementationdetailsm_countPerLock/Implementationdetailsm_comparer/Implementationdetails
Task-basedAsynchronousPattern(TAP)about/Task-basedAsynchronousPatternfeatures/Task-basedAsynchronousPattern
taskcancellationabout/Taskcancellationflag,checking/Checkingaflagexception,throwing/ThrowinganexceptionOSwaitobjects,usingwithWaitHandle/UsingOSwaitobjectswithWaitHandlewithcallbacks/Cancellationusingcallbacks
taskcompositionabout/Taskcomposition
TaskParallelLibraryparallelsolution,creatingwith/CreatingaparallelsolutionwithTaskParallelLibrary
TaskParallelLibrary(TPL)about/Understandinggranularity
TaskParallelLibrary,featuresabout/OtherusefulTPLfeaturesTask.Delay/Task.DelayTask.Yield/Task.Yield
taskschedulerabout/Taskcomposition,Understandingthetaskscheduler
taskshierarchyabout/Taskshierarchy
testswriting/Writingtestsloadtests/Loadtestsunittests/Unittests
Thread.SpinWaitabout/Thread.SpinWait
threadcontention/Standardcollectionsandsynchronizationprimitivesthreadpool
using/Usingthethreadpoolthreads
overview/Thenumberofthreads
www.EBooksWorld.ir
![Page 385: Mastering C# Concurrency - EBooksWorld...a print book customer, you are entitled to a discount on the eBook copy. Get in touch with us at for more details](https://reader035.vdocument.in/reader035/viewer/2022062917/5ed62e6504e9cb4adb670aed/html5/thumbnails/385.jpg)
UUIthread/UIthreadsandmessageloops
www.EBooksWorld.ir
![Page 386: Mastering C# Concurrency - EBooksWorld...a print book customer, you are entitled to a discount on the eBook copy. Get in touch with us at for more details](https://reader035.vdocument.in/reader035/viewer/2022062917/5ed62e6504e9cb4adb670aed/html5/thumbnails/386.jpg)
Vvolatile
about/Memorymodelandcompileroptimizationsvolatilekeyword
about/Memorymodelandcompileroptimizationsvolatileread
about/Memorymodelandcompileroptimizationsvolatilewrite
about/Memorymodelandcompileroptimizations
www.EBooksWorld.ir
![Page 387: Mastering C# Concurrency - EBooksWorld...a print book customer, you are entitled to a discount on the eBook copy. Get in touch with us at for more details](https://reader035.vdocument.in/reader035/viewer/2022062917/5ed62e6504e9cb4adb670aed/html5/thumbnails/387.jpg)
WWaitHandle
OSwaitobjects,usingwith/UsingOSwaitobjectswithWaitHandlewaitingqueue
about/Monitorclasswhileloop/Fine-grainedlockoperations,ProcessTasksinCompletionOrderWindowsForms
about/Creatingasimplesynchronoussolutionworkerthreads/Usingthethreadpoolworkstealing/ConcurrentBag<T>
www.EBooksWorld.ir
![Page 388: Mastering C# Concurrency - EBooksWorld...a print book customer, you are entitled to a discount on the eBook copy. Get in touch with us at for more details](https://reader035.vdocument.in/reader035/viewer/2022062917/5ed62e6504e9cb4adb670aed/html5/thumbnails/388.jpg)
Xxampp
about/Loadtestingandscalability
www.EBooksWorld.ir