Tải bản đầy đủ - 0 (trang)
Chapter 12.  Know when and how to code for concurrency

Chapter 12.  Know when and how to code for concurrency

Tải bản đầy đủ - 0trang

Summary

Thsareafedly:Ifyourapplicationusesmultiplethreadsor

processes,knowhowtominimizesharingobjectswhere

possible(seeItem10)andsharetherightonessafely.



Discussion

Threadingisahugedomain.ThisItemexistsbecausethat

domainisimportantandneedstobeexplicitlyacknowledged,

butoneItemcan'tdoitjusticeandwewillonlysummarizea

fewessentials;seetheReferencesformanymoredetailsand

techniques.Amongthemostimportantissuesaretoavoid

deadlocks,livelocks,andmalignraceconditions(including

corruptionduetoinsufficientlocking).

TheC++Standardsaysnotonewordaboutthreads.

Nevertheless,C++isroutinelyandwidelyusedtowritesolid

multithreadedcode.Ifyourapplicationsharesdataacross

threads,dososafely:

Consultyourtargetplatforms'documentationforlocal

synchronizationprimitives:Typicalonesrangefrom

lightweightatomicintegeroperationstomemorybarriersto

in-processandcross-processmutexes.

Prefertowraptheplatform'sprimitivesinyourown

abstractions:Thisisagoodideaespeciallyifyouneed

cross-platformportability.Alternatively,youcanusea

library(e.g.,pthreads[Butenhof97])thatdoesitforyou.

Ensurethatthetypesyouareusingaresafetouseina

multithreadedprogram:Inparticular,eachtypemustat

minimum:

Guaranteethatunsharedobjectsareindependent:Two

threadscanfreelyusedifferentobjectswithoutany

specialactiononthecaller'spart.

Documentwhatthecallerneedstodotousethesame



objectofthattypeindifferentthreads:Manytypeswill

requireyoutoserializeaccesstosuchsharedobjects,

butsometypesdonot;thelattertypicallyeitherdesign

awaythelockingrequirement,ortheydothelocking

internallythemselves,inwhichcase,youstillneedto

beawareofthelimitsofwhattheinternallocking

granularitywilldo.

Notethattheaboveappliesregardlessofwhetherthetype

issomekindofstringtype,oranSTLcontainerlikea

vector,oranyothertype.(Wenotethatsomeauthors

havegivenadvicethatimpliesthestandardcontainersare

somehowspecial.Theyarenot;acontainerisjustanother

object.)Inparticular,ifyouwanttousestandardlibrary

components(e.g.,string,containers)inamultithreaded

program,consultyourstandardlibraryimplementation's

documentationtoseewhetherthatissupported,as

describedearlier.

Whenauthoringyourowntypethatisintendedtobeusablein

amultithreadedprogram,youmustdothesametwothings:

First,youmustguaranteethatdifferentthreadscanuse

differentobjectsofthattypewithoutlocking(note:atypewith

modifiablestaticdatatypicallycan'tguaranteethis).Second,

youmustdocumentwhatusersneedtodoinordertosafely

usethesameobjectindifferentthreads;thefundamental

designissueishowtodistributetheresponsibilityofcorrect

execution(race-anddeadlock-free)betweentheclassandits

client.Themainoptionsare:

Externallocking:Callersareresponsibleforlocking.Inthis

option,codethatusesanobjectisresponsibleforknowing

whethertheobjectissharedacrossthreadsand,ifso,for

serializingallusesoftheobject.Forexample,stringtypes

typicallyuseexternallocking(orimmutability;seethethird

optiononthenextpage).



Internallocking:Eachobjectserializesallaccesstoitself,

typicallybylockingeverypublicmemberfunction,sothat

callersmaynotneedtoserializeusesoftheobject.For

example,producer/consumerqueuestypicallyuseinternal

locking,becausetheirwholeraisond'ờtreistobeshared

acrossthreads,andtheirinterfacesaredesignedsothatthe

appropriateleveloflockingisforthedurationofindividual

memberfunctioncalls(Push,Pop).Moregenerally,note

thatthisoptionisappropriateonlywhenyouknowtwo

things:

First,youmustknowupfrontthatobjectsofthetypewill

nearlyalwaysbesharedacrossthreads,otherwiseyou'll

endupdoingneedlesslocking.Notethatmosttypesdon't

meetthiscondition;thevastmajorityofobjectsevenina

heavilymultithreadedprogramareneversharedacross

threads(andthisisgood;seeItem10).

Second,youmustknowupfrontthatper-member-function

lockingisattherightgranularityandwillbesufficientfor

mostcallers.Inparticular,thetype'sinterfaceshouldbe

designedinfavorofcoarse-grained,self-sufficient

operations.Ifthecallertypicallyneedstolockseveral

operations,ratherthananoperation,thisisinappropriate;

individuallylockedfunctionscanonlybeassembledintoa

larger-scalelockedunitofworkbyaddingmore(external)

locking.Forexample,consideracontainertypethatreturns

aniteratorthatcouldbecomeinvalidbeforeyoucoulduse

it,orprovidesamemberalgorithmlikefindthatcanreturn

acorrectanswerthatcouldbecomethewronganswer

beforeyoucoulduseit,orhasuserswhowanttowriteif(

c.empty())c.push_back(x);.(See[Sutter02]for

additionalexamples.)Insuchcases,thecallerneedsto

performexternallockinganywayinordertogetalock

whoselifetimespansmultipleindividualmemberfunction

calls,andsointernallockingofeachmemberfunctionis

needlesslywasteful.



So,internallockingistiedtothetype'spublicinterface:

Internallockingbecomesappropriatewhenthetype's

individualoperationsarecompleteinthemselves;inother

words,thetype'slevelofabstractionisraisedand

expressedandencapsulatedmoreprecisely(e.g.,asa

producer-consumerqueueratherthanaplainvector).

Combiningprimitiveoperationstogethertoformcoarser

commonoperationsistheapproachneededtoensure

meaningfulbutsimplefunctioncalls.Wherecombinationsof

primitivescanbearbitraryandyoucannotcapturethe

reasonablesetofusagescenariosinonenamedoperation,

therearetwoalternatives:a)useacallback-basedmodel

(i.e.,havethecallercallasinglememberfunction,butpass

inthetasktheywantperformedasacommandorfunction

object;seeItems87to89);orb)exposelockinginthe

interfaceinsomeway.

Lock-freedesigns,includingimmutability(read-only

objects):Nolockingneeded.Itispossibletodesigntypes

sothatnolockingatallisneeded(seeReferences).One

commonexampleisimmutableobjects,whichdonotneed

tobelockedbecausetheyneverchange;forexample,for

animmutablestringtype,astringobjectisnevermodified

oncecreated,andeverystringoperationresultsinthe

creationofanewstring.

Notethatcallingcodeshouldnotneedtoknowaboutyour

types'implementationdetails(seeItem11).Ifyourtypeuses

under-the-coversdata-sharingtechniques(e.g.,copy-on-write),

youdonotneedtotakeresponsibilityforallpossiblethread

safetyissues,butyoumusttakeresponsibilityforrestoring

"justenough"threadsafetytoguaranteethatcallingcodewill

becorrectifitperformsitsusualdutyofcare:Thetypemust

beassafetouseasitwouldbeifitdidn'tusecovert

implementation-sharing.(See[Sutter04c].)Asnoted,all

properlywrittentypesmustallowmanipulationofdistinct

visibleobjectsindifferentthreadswithoutsynchronization.



Particularlyifyouareauthoringawidely-usedlibrary,consider

makingyourobjectssafetouseinamultithreadedprogramas

describedabove,butwithoutaddedoverheadinasinglethreadedprogram.Forexample,ifyouarewritingalibrary

containingatypethatusescopy-on-write,andmusttherefore

doatleastsomeinternallocking,prefertoarrangeforthe

lockingtodisappearinsingle-threadedbuildsofyourlibrary

(#ifdefsandno-opimplementationsarecommonstrategies).

Whenacquiringmultiplelocks,avoiddeadlocksituationsby

arrangingforallcodethatacquiresthesamelockstoacquire

theminthesameorder.(Releasingthelockscanbedoneinany

order.)Onesolutionistoacquirelocksinincreasingorderby

memoryaddress;addressesprovideahandy,unique,

application-wideordering.



References

[Alexandrescu02a][Alexandrescu04][Butenhof97]

[Henney00][Henney01][Meyers04][Schmidt01]

[Stroustrup00]Đ14.9[Sutter02]Đ16[Sutter04c]



13.Ensureresourcesareownedby

objects.UseexplicitRAIIandsmart

pointers

Summary

Discussion

Exceptions

References



Summary

Don'tsawbyhandwhenyouhavepowertools:C++'s

"resourceacquisitionisinitialization"(RAII)idiomisthepower

toolforcorrectresourcehandling.RAIIallowsthecompilerto

providestrongandautomatedguaranteesthatinother

languagesrequirefragilehand-codedidioms.Whenallocatinga

rawresource,immediatelypassittoanowningobject.Never

allocatemorethanoneresourceinasinglestatement.



Discussion

C++'slanguage-enforcedconstructor/destructorsymmetry

mirrorsthesymmetryinherentinresourceacquire/release

functionpairssuchasfopen/fclose,lock/unlock,and

new/delete.Thismakesastack-based(orreference-counted)

objectwitharesource-acquiringconstructorandaresourcereleasingdestructoranexcellenttoolforautomatingresource

managementandcleanup.

Theautomationiseasytoimplement,elegant,low-cost,and

inherentlyerror-safe.Ifyouchoosenottouseit,youare

choosingthenontrivialandattention-intensivetaskofpairing

thecallscorrectlybyhand,includinginthepresenceof

branchedcontrolflowsandexceptions.SuchC-stylerelianceon

micromanagingresourcedeallocationisunacceptablewhen

C++providesdirectautomationviaeasy-to-useRAII.

Wheneveryoudealwitharesourcethatneedspaired

acquire/releasefunctioncalls,encapsulatethatresourceinan

objectthatenforcespairingforyouandperformstheresource

releaseinitsdestructor.Forexample,insteadofcallingapairof

OpenPort/ClosePortnonmemberfunctionsdirectly,consider:



classPort{

public:

Port(conststring&destination);//callOpenPort

~Port();//callClosePort

//portscan'tusuallybecloned,sodisablecopyingandas

};



voidDoSomething(){

Portport1("server1:80");

//

}//can'tforgettocloseport1;it'sclosedautomaticallyatt



shared_ptrport2=/**/;//port2isclosedautomatica

//lastshared_ptrreferring



Youcanalsouselibrariesthatimplementthepatternforyou

(see[Alexandrescu00c]).

WhenimplementingRAII,beconsciousofcopyconstructionand

assignment(seeItem49);thecompiler-generatedversions

probablywon'tbecorrect.Ifcopyingdoesn'tmakesense,

explicitlydisablebothbymakingthemprivateandnotdefined

(seeItem53).Otherwise,havethecopyconstructorduplicate

theresourceorreference-countthenumberofuses,andhave

theassignmentoperatordothesameandensurethatitfrees

itsoriginallyheldresourceifnecessary.Aclassicoversightisto

freetheoldresourcebeforethenewresourceissuccessfully

duplicated(seeItem71).

Makesurethatallresourcesareownedbyobjects.Preferto

holddynamicallyallocatedresourcesviasmartpointersinstead

ofrawpointers.Also,performeveryexplicitresourceallocation

(e.g.,new)initsownstatementthatimmediatelygivesthe

allocatedresourcetoamanagerobject(e.g.,shared_ptr);

otherwise,youcanleakresourcesbecausetheorderof

evaluationofafunction'sparametersisundefined.(SeeItem

31.)Forexample:

voidFun(shared_ptrsp1,shared_ptrsp2);

//

Fun(shared_ptr(newWidget),shared_ptr(



Suchcodeisunsafe.TheC++Standardgivescompilersgreat

leewaytoreorderthetwoexpressionsbuildingthefunction's

twoarguments.Inparticular,thecompilercaninterleave



Tài liệu bạn tìm kiếm đã sẵn sàng tải về

Chapter 12.  Know when and how to code for concurrency

Tải bản đầy đủ ngay(0 tr)

×