Threading demo
Door Rutger Smit
19 september 2004
Demo ontwikkeld voor ASPNL.com in Visual Studio.NET 2003.
Code geschreven in C# en gebaseerd op het .NET Framework versie 1.1.
Inleiding
Dit artikel gaat over een geweldige nieuwe mogelijkheid die het .NET Framework
ons biedt voor ASP.NET applicaties: Threading. In de demo pagina’s die bij dit artikel horen
gaan we zien dat langdurige processen zonder heel veel moeite gestart kunnen worden
vanaf een pagina zonder dat de bezoeker van die pagina op respons van de webserver hoeft
te wachten.
Wat is een Thread?
Applicaties worden uitgevoerd door een proces. Een proces is een entiteit binnen
het systeem dat z'n eigen systeembronnen heeft, en tijd krijgt om een applicatie
uit te voeren. Een proces is geheel gescheiden van andere processen in het systeem,
zodat meerdere applicatie tegelijk uitgevoerd kunnen worden. Code in een applicatie
wordt uitgevoerd door een thread, en elk proces bevat dan ook minimaal één
thread. Applicaties die maar één thread gebruiken noemen we single-threaded.
ASP.NET is echter multi-threaded, want als er maar één thread zou zijn, dan
zouden alle requests één-voor-één uitgevoerd moeten worden, en
zou een pagina die heel kort duurt moeten wachten totdat alle voorgaande pagina's klaar zijn.
Om dat te voorkomen heeft ASP.NET een zogenaamde thread-pool (een verzameling threads die
gebruikt kunnen worden om een request af te handelen). Wanneer een request gemaakt wordt,
gebruikt ASP.NET een van de threads in de thread-pool om een pagina uit te voeren. Een pagina
wordt normaal gezien dus door één thread uitgevoerd en is daarom normaal gezien
single-threaded.
Multi-threading in ASP.NET
Omdat een ASP.NET pagina single-threaded is hebben we een probleempje als het gaat om
het uitvoeren van langdurige operaties. Een operatie die op de server enkele
secondes duurt laat de client (de bezoeker van de website) al die tijd wachten.
Daar komt nog eens de tijd van het versturen van de output van server naar client
bij plus de tijd die de browser nodig heeft om de html te parsen tot een
zichtbare pagina. Op het moment dat je bepaalde logica wilt uitvoeren die meer
tijd in beslag neemt, bijvoorbeeld meer dan 10 seconden, zal de client al die
tijd wachten op een antwoord van de server. In de meeste gevallen zal er na 90
seconden een server timeout plaats vinden. Echter, de meeste mensen zijn na 20
seconden het wachten al zat en sluiten hun venster of klikken op Refresh waardoor
de code nogmaals uitgevoerd wordt.
In classic asp had je dan een probleem. Meestal werd er omheen gehacked door het
in batches uit te voeren, de Script.TimeOut extreem hoog te zetten en/of de
pagina te tweaken met Response.Buffer/Response.Flush. Gelukkig kunnen we in ASP.NET
gebruik maken van Threading, en kunnen we een operatie laten uitvoeren door een
andere thread dan de thread die de pagina zelf uitvoert. Hierdoor kan de thread die
de pagina uitvoert gewoon verder.
Hoe het werkt
Ok, hands on! Hieronder volgt een bespreking van de code die ik voor deze demo
geschreven heb. Het proces dat bij mij veel tijd in beslag neemt is Thread.Sleep
(5000); dit geeft het zelfde effect als een immense loop door records of een
andere operatie dat veel tijd in beslag neemt. Wil je de code van deze demo
toepassen op je eigen ASP.NET pagina dan hoef je op de plaats waar ik Thread.Sleep
(5000); heb staan alleen maar je eigen code te plaatsen.
In de demo zitten de volgende pagina’s waarvan de werking hieronder uitgelegd
wordt:
Default.aspx(.cs)
Openingspagina met een link naar de pagina zonder threading en een link naar een
pagina die hetzelfde doet maar dan met threading. Tevens nog een link naar een
zip file met daarin de code en dit PDF document.
ZonderThreading.aspx(.cs)
Pagina met de code voor het uitvoeren van een langdurige operatie zonder
threading.
MetThreading.aspx(.cs)
Pagina met de code voor het uitvoeren van een langdurige operatie met threading.
Polling.aspx(.cs)
Deze pagina wordt in MetThreading.aspx gebruikt om te bewijzen dat de code van
MetThreading.aspx daadwerkelijk functioneert.
ZonderThreading.aspx(.cs)
Allereerst moeten we de namespace System.Threading importeren om later een langdurige
operatie te kunnen simuleren. In een pagina met een langdurig proces maar zonder
threading is deze dus niet nodig! In void Button1_Click schrijven we eerst wat
informatie naar een TextBox (LogTextBox) om inzicht te krijgen in de tijd die
bepaalde acties in de code in beslag neemt. Tussen deze twee tijden voeren we de
method StartProces() uit. Dit is in deze demo het process dat veel tijd in beslag
zal gaan nemen. In StartProces() voeren we Thread.Sleep(5000) uit om het
langdurige proces te simuleren. Op deze plaats kun je eigen code plaatsen die
veel tijd in beslag neemt.
Als je deze pagina in je browser bekijkt en op de button klikt wordt de code
uitgevoerd. In de textbox zul je twee tijden zien staan, de tijd voordat de
operatie gestart werd en de tijd dat de operatie klaar was. In de demo zal het
verschil 5 seconden zijn (5000 milliseconden).
MetThreading.aspx(.cs)
Ook in dit bestand moeten we System.Threading importeren, en ditmaal niet alleen
om een langdurige operatie te simuleren maar ook om een nieuwe thread op te starten.
Net als in ZonderThreading.aspx schrijven we eerst wat informatie naar een
textbox voordat we het langdurige proces opstarten. Vervolgens maken we een
nieuwe thread aan en starten deze:
ThreadStart entry = new ThreadStart(StartProces) ;
Thread thread = new Thread(entry);
thread.Priority= ThreadPriority.Lowest;
thread.Start();
Let hierbij op dat StartProces zonder () geschreven wordt! Omdat deze thread op de achtergrond
draait en we de website niet willen vertragen zetten we de priority op ThreadPriority.Lowest.
Aan het einde van de method StartProces() schrijven we de huidige tijd naar het Cache object,
deze wordt in polling.aspx weer opgevraagd om te kunnen laten zien dat de operatie afgerond is
(zie hieronder bij het bespreken van polling.aspx).
Let op: omdat het een separate thread is kunnen we geen gebruik maken van objecten die in een
andere scope draaien zoals Session, Request en Response.
Polling.aspx(.cs)
Dit bestand wordt in een Iframe in MetThreading.aspx geladen. De pagina heeft een
Refresh van 2 seconden om de status van de langdurige operatie te controleren. In
de Page_Load event van dit bestand wordt de inhoud van Cache["ProcessEnd"] naar
een label geschreven. In situaties waar we threading meestal gebruiken komen er
geen user interface zaken aan de orde, maar in deze demo is het handig om op een
simpele manier te laten zien dat een thread op de achtergrond door draait.
Overige bestanden
De overige bestanden worden standaard door Visual Studio.NET aangemaakt en hebben
geen directe relatie met de code uit deze demo.
Conclusie
Door gebruik te maken van threading kunnen we code die veel tijd in beslag neemt
om uitgevoerd te worden op een slimme manier afhandelen zonder daarbij
het geduld van de client op de proef te stellen. Het is daarom een zeer bruikbare
mogelijkheid die het .NET Framework ons biedt.
|