Vrijwel alle multithreaded applicaties welke ik schrijf c.q. onderhoud zijn gebaseerd op distributed application designs. Elke thread kan gezien worden als een 'standalone' applicatie. De hoofd thread fungeert als dispatcher om de threads te starten.
Locking kun je ook deels voorkomen door een event handler te gebruiken. Via ISynchronizeInvoke kun je dan het event naar de hoofdtread sturen en de hoofdthread is de enigste welke een locking op de collectie uitvoert om het resultaat op te slaan. Er zijn verschillende methoden om synchronisatie te voorkomen.
Locking is opzich geen probleem zolang slechts 1 thread de locking uitvoert. Multi threaded locking hebben namelijk kernel hooks nodig en gaan daardoor buiten de CLR om. Locking op de hoofd thread (het process welke is opgestart door de CLR) wordt door de CLR uitgevoerd zolang er geen multi threaded lock nodig is.
Door alleen locking op de hoofd thread te gebruiken (in de dispatcher en event handler) is het vrij eenvoudig om multi threaded synchronisatie te voorkomen. Zelf ontwikkel ik geen threads welke weer afhankelijk zijn van andere threads.
Wat betreft de CTP versies. Geen van de vorige versies heeft een verandering gebracht in bestaande API. Elke CTP bevat eigenlijk vooral meer speelgoed voor multi threaded applicaties.
Stel je wilt een matrix van 10x10 printen. Je zou via een loop 10 maal een thread kunnen starten welke elk een rij afdrukt. Maar waarschijnlijk is de winst dan vrij minimaal. De meeste threaded zullen dan de CPU power moeten delen en de overhead zal een groot deel van de performance winst teniet doen. Via een Semaphore in de loop slecht 2 of 3 theads starten zal in de meeste gevallen meer winst opleveren (de CPU schedular is dan minder kwijt de resources te verdelen). Voor de meeste applicaties zijn threads voornamelijk interessant als er veel idle tijd is (bijv. wachten op database of netwerk response). Rekenkundige threads hebben over het algemeen weinig idle (lees: alle threads willen processor tijd) tijd en zal de winst minimaal zijn. Rekenkundige threads halen de meeste winst als zij worden verdeeld over meerdere cores. Echter verplaatst de CLR deze threads allemaal naar dezelfde thread zodra er threads gesynchroniseerd dienen te worden.
Al onze multi threaded applicaties hebben een ingebouwde 'profiler' test procedure. Deze profiler test voert de verschillende kerntaken 100 maal uit. De profiler gebruikt na elke run een thread meer totdat de uitvoer langer duurt dan de vorige. Elke run wordt driemaal uitgevoerd en het gemiddelde uitvoeringstijd wordt gebruikt voor de vergelijking. De tests slaan uiteindelijk de optimale waardes op in de app.config en vormen daarmee de maximum waarde van de semaphores.
Soms zijn synchronisaties echter niet te voorkomen. Anders dan in C/C++ regelt de CLR heel veel zaken voor jouw waar je geen of weinig invloed op hebt. Een van die zaken is de fallback van multi threaded apartment naar single threaded apartment (applicaties draaien dan op een enkele core). De processor instructie pipelines worden ook 'threads' genoemd.
Distributed application design patterns vormen vaak een goede basis voor een multi threaded applicatie. Een aantal goede design patterns hiervoor zijn te vinden op
http://www.cs.wustl.edu/~schmidt/patterns-ace.html (C++ based, maar theorie is ook bruikbaar voor c#/vb developers) en op
http://www.cmcrossroads.com/bradapp/javapats.html zijn distributed design patterns (en de Gang of Four patterns) voor java.
If it isn't broken, fix it until it is..