870 likes | 1.05k Views
SDP - Repetition. Sockets Trådning Delegater Remoting Sprogteori. Sockets. Logiske endepunkter mellem server og klient På applikationsniveauet betragtes Sockets som pålidelige Sockets optræder parvis Til en Socket er der via en NetworkStream knyttet både en StreamReader StreamWriter
E N D
SDP - Repetition • Sockets • Trådning • Delegater • Remoting • Sprogteori
Sockets • Logiske endepunkter mellem server og klient • På applikationsniveauet betragtes Sockets som pålidelige • Sockets optræder parvis • Til en Socket er der via en NetworkStream knyttet både en • StreamReader • StreamWriter • Klientens StreamReader er forbundet til serverens StreamWriter og omvendt
Sockets - Serversiden • Skab en TcpListener på port 20001 (fx) TcpListener server = new TcpListener(IPAddress.Any, 20001); server.Start(); • Vent på forespørgsel fra klient while(true) { Socket klient = server.AcceptSocket(); } • Skab StreamReader og StreamWriter ud fra Socket NetworkStream stream = new NetworkStream(klient); StreamReader reader = new StreamReader(stream); StreamWriter writer = new StreamWriter(stream); writer.AutoFlush = true; • Send og modtag via StreamReader og StreamWriter reader.ReadLine(); writer.WriteLine(); • Luk strømme og Sockets
Sockets - Klientsiden • Skab en Socket til serveren TcpClient server = new TcpClient(”localhost”, 20001); • Skab StreamReader og StreamWriter ud fra Socket NetworkStream stream = server.GetStream(); StreamReader reader = new StreamReader(stream); StreamWriter writer = new StreamWriter(stream); writer.AutoFlush = true; • Send og modtag via StreamReader og StreamWriter reader.ReadLine(); writer.WriteLine(); • Luk strømme og Socket
Flere samtidige klienter • Server class Server { publicstaticvoid) { TcpListener server = new TcpListener(IPAddress.Any, 20001) server.Start(); while (true) { Socket klient = server.AcceptSocket(); new Thread(new ThreadStart(new ClientHandler(klient).Run)).Start(); } } }
Flere samtidige klienter • ClientHandler class ClientHandler { private Socket incoming; public ClientHandler(Socket incoming) { this.incoming = incoming; } publicvoid Run() { // Instantiér ind- og ud-strøm while (true) { // Kommunikér } } }
Hvorfor trådning? • Performance • Responsivness
Succeskriterier • Forbedring af performance • Opgaverne skal være uafhængige • Jo flere afhængige opgaver, jo mindre forbedring • Forbedring af responsivness • Responsivness er et spørgsmål om oplevelse • Jo længere tid en opgave tager, jo mindre virksom opleves applikationen, hvis brugeren ikke har noget at lave eller se på
Fordele • Overvågningssystemer med flere uafhængige opgaver • Muliggør brugerinteraktion via BGF • Muliggør serverhåndtering af flere samtidige klienter • Udnytter flerprocessor-systemer optimalt
Ulemper • Trådene afvikles uafhængigt af hverandre • Afviklingsrækkefølgen synes vilkårlig • Samspillet mellem tråde er ofte meget kompleks • Problemer med safety og liveness
Trådskifte • CPU’en skifter til en ny tråd … • Efter et givent tidsrum, eller • Når en tråd venter på færdiggørelsen af en opgave, fx en I/O-operation …
Processer vs. Tråde • Der er mindst to former for samtidighed: • Multiprocessing • Multithreading
Multiprocessing • Samtidighed på tværs af applikationer • Til heavy-weight opgaver(IIS, SQL, Server, …) • Hver proces eksekverer én enkelt tråd
Multithreading • Samtidighed i applikationer • Forskellige tråde udfører forskellige opgaver • Perfekt til light-weight opgaver(Metodekald, UI-opdateringer, …)
Multithreading • Samtidighed i .NET • Tråde håndteres af CLR • Tråde har deres egen stak (locals), men deler globals og heap
Instantiering af tråde • Tråde kaldes ikke, de startes … • Man kan derfor ikke medsende parametre, modtage returværdi og fange exceptions på sædvanlig vis • Trådet kode skal håndtere fejl … • Uhåndterede fejl vil terminere applikationen • Gør designet simpelt … • Trådning er et minefelt – fælles ressourcer giver anledning til kritiske sektioner, race conditions, deadlock, …
Instantiering af tråde publicstaticvoid Main() { Thread t; t = new Thread(new ThreadStart(Run)); t.Start() } publicstaticvoid Run() { // kode }
Trådsikre klasser • Problem • Hvis en tråd afbrydes, medens den arbejder på et objekt, er der risiko for, at objektet efterlades i eninkonsistent tilstand.Dette kan give problemer, når en anden tråd forsøger at få tilgang til objektet
Trådsikre klasser • Løsning • Løsningen er at forhindre mere end én tråd ad gangen i at få tilgang til de kritiske områder i koden
Single Threaded Execution • Vha. lock(obj) opnår en tråd eneret på et objekt – en lås • Låsen frigives, når tråden forlader det kritiske område eller ved kald af metoden Monitor.Wait(obj) på objektet. • Kritiske områder på samme objekt deler samme lås!
Liveness problemer • Starvation (udhungring) • En tråd, som er i tilstanden ready, får aldrig lejlighed til at køre, fordi der findes andre tråde med højere prioritet • Dormancy (dvale) • En tråd, som er i tilstandenblocked on wait, vækkes aldrig med Monitor.Pulse() • Deadlock (hårdknude) • To eller flere tråde kæmper om flere fælles ressourcer, og hver tråd efterspørger på samme tid disse ressourcer • Premature Termination (for tidlig død) • En tråd termineres for tidligt og hindrer derved andre tråde i at blive vækket. (Evig dvale)
Brug af lock() • Nødvendig, hvis man vil lave "trådsikre klasser" • Ej omkostningsfrit:Kræver CPU-kraft langsommere programafvikling • "Hellere for mange synkroniserede blokke end for få!"MEN - pas på deadlocks
lock vs. Monitor.Enter/Exit • lock(obj) er et alias for: try { Monitor.Enter(obj); // kode ... Monitor.Exit(obj); } catch { } finally { Monitor.Exit(obj); }
Monitor • Monitor • indkapsler fælles ressourcer som private attributter og sikrer trådene enetilgang vha. lock() til de kritiske områder • Formål • Overvågning af trådes adgang til fælles ressourcer
Monitor • Objekt, hvor de fælles ressourcer er samlet som attributter • Der er udelelig adgang til monitorens kritiske områder • Én monitor-lock pr. kritiskområde
Early notification • Problem • Monitor.Pulse() sendes før betingelserne for Monitor.Wait er opfyldt • Konsekvens • Wait-tråden vækkes før tid • Løsning • Gentjek betingelserne for Monitor.Wait, når Wait-tråden vækkes • Konklusion • Anvend altid while (i stedet for if) i forbindelse med check af wait-betingelser
Early notification • Bufferen er tom! • C1-tråden er i Wait-tilstand • P-tråden generer et nyt element og kalder monitorens synkroniserede put-metode • C1-tråden underrettes via Pulse, og tråden går i ready-tilstand • P-tråden forlader monitorens put-metode og slipper objekt-låsen på monitoren • Bufferen indeholder nu ét element! • Trådskifte! • C2-tråden trækker et element fra bufferen ved at kalde monitorens get-metode • C2-tråden forlader monitorens get-metode og slipper objekt-låsen på monitoren • Der er ingen elementer i bufferen! • Trådskifte! • C1 fortsætter nu fra Wait i monitorens get-metode i forsøget på at trække endnu et element fra bufferen … Dette er ikke muligt!
Guarded Suspension publicvoid Put(int data) { lock(this) { while (queue.Full) Monitor.Wait(this); if (queue.Empty) Monitor.PulseAll(this); this.queue.Enqueue(data); } }
Guarded Suspension publicvoid Get(int data) { lock(this) { while (queue.Empty) Monitor.Wait(this); if (queue.Full) Monitor.PulseAll(this); this.queue.Dequeue(data); } }
Design af trådet applikation • Anvend Producer-Consumer mønstret • Anvend de 10 gode trådråd • Husk den gyldne regel
10 gode trådråd • Bestem tråde • Bestem trådmetoder • Bestem fælles ressourcer • Bestem monitorer - Én monitor for hver uafhængig fælles ressource • Bestem monitor-klasser • Bestem kritiske sektioner • Bestem Wait/Pulse – Et par for hver tilstand, som kræver, at en tråd venter på en anden • Fordel Wait og Pulse (Husk ”Guarded Suspension”-mønstret) • Lav pseudo-kode for monitor-klassernes metoder • Lav pseudo-kode for tråd-klassernes run-metoder
Den gyldne UI-regel • UI-baserede .NET-applikationer • Den tråd, som har skabt UI’en ejer UI’en – Ingen anden tråd må tilgå UI’ens kontrolelementer …
Delegater • Delegater er objekter, der refererer til en metode – tænk ”funktionspointer” delegate private void MinMethod(...) { . . . } • - med delegater kan man kalde en underliggende metode på en typesikker måde • - med delegater kan man arbejde med metodereferencer på en objektorienteret facon
delegate Window Forms • Hvordan ved en knap, hvilken metode den skal kalde ved et click-event? • Det ved den heller ikke! Den kalder metoden bagved delegaten … Button Click: private void btnAdd_Click(object sender, EventArgs e) { int i, j, k; i = int.Parse(this.txtNumber1.Text); j = int.Parse(this.txtNumber2.Text); k = i + j; System.Windows.Forms.MessageBox.Show("Sum = " + k); }
Delegat-baseret kode i VS . . . this.btnAdd = new Button(); this.btnAdd.Click += new System.EventHandler(this.btnAdd_Click);
Remoting Server.exe (.NET) Client.exe (.NET) • Klienten ser serveren som en assembly (dll) • Referencen sættes på sædvanlig vis … • Serveren gør dll’en tilgængelig på flg. måder • Tjeneste på en server, der svarer på kald fra klienter. Effektivt! • Integreret i en web-tjeneste, der kaldes via URL. ”Firewall-venligt”!
Komponent-dll • MBR-objekter arver fra MarshalByRefObject • MBV-objekter skal være Serializable namespace Komponent { public class Beregningsobjekt : System.MarshalByRefObject{ private static int Objekttæller = 0; private int ObjektID; // Konstruktør public Beregningsobjekt() { this.ObjektID = ++Objekttæller; } public DataObjekt HentDataObject() { return new DataObjekt(); } } namespace Komponent { [System.Serializable()] public class DataObjekt{ . . . } }
Server Client stub proxy remoting remoting formatter channel formatter listener Remoting - arkitektur • Generel arkitektur • Proxy, stub, formattering og channel kan ændres. Fx er en binær formattering, der både understøttes af TCP og HTTP, mere effektiv end en tekstbaseret (XML)
Remoting - Design • Designbeslutning: MBRO eller MBVO? • Med andre ord: ”Hvor skal objekterne leve?” • MBRO lever på serveren • MBVO lever på klienten • Gængs praksis: • Beregninger sker på serveren MBRO • Fx søgning og køb på en ehandel • Tilstande bevares på klienten MBVO • Fx indkøbskurv på en ehandel håndteres af klienten
MBRO - muligheder • Server-activated object (SAO)også betegnet ”Wellknown objects” • Singleton: Ét serverobjekt til alle klienter • Trådproblemer • SingleCall: Ét objekt pr. metodekald • Nemmeste løsning; men mindst effektivt • Tilstand opretholdes vha. filer eller database • Client-activated object (CAO) • Activated: Ét objekt pr. klient • Levetidsproblem – Leasing understøttes ikke af IIS
MBVO - muligheder • Egenudviklede klasser • Kan udføre special-opgaver på klienten • .NET collection og db-klasser • Eksempelvis DataSet er en meget brugbar klasse til at overføre data …
Minimér udrulning på klienten • Hvad skal udrulles på klienten? • Som udgangspunkt: • Konkrete implementationer af alle MBRO- og MBVO-klasser • Dette vil vi som regel gerne minimere … • Mulige tilgange: • Definér MBRO vha. interfaces og udrul Interface.dll • Definér MBVO vha. .NET-klasser som fx DataSet
Design - Server • Minimér antal kald fra klient til server, da: • Ethvert kald går via netværket • Ethvert kald er et potentielt nyt server-objekt • Ethvert kald involverer marshalling • Enhver reference til en property er også et kald • Svartider er uforudsigelige … • Retningslinjer: • Færre og større metoder • Metoder er transaktioner – Commit/Rollback før return
Design - Klient • Anvend factory-klasser og .config-filen • Factory-klasser skjuler instantieringslogik • Konfigurationsfilen kan specificere URL, formattering, channels, osv. • Konfigurationsfilen kan redirigere new til at instantiere på serveren
MathLibrary.dll namespace MathLibrary { public class SimpleMath : MarshalByRefObject{ private static int classCount = 0; private int instanceCount; public SimpleMath() { this.instanceCount = ++SimpleMath.classCount; } public int Add(int a, int b) { return a + b; } public int Sub(int a, int b) { return a - b; } public int Count { get { return this.instanceCount; } } } }
SAO - Wellknown Objects namespace MathServer { class Program { static void Main(string[] args) { ChannelServices.RegisterChannel(new HttpChannel(20001), false); RemotingConfiguration.RegisterWellKnownServiceType(typeof(SimpleMath),"SimpleMath.soap",WellKnownObjectMode.SingleCall); } } } namespace MathKlient { class Program { static void Main(string[] args) {RemotingConfiguration.RegisterWellKnownClientType(typeof(SimpleMath),"http://localhost:20001/SimpleMath.soap");SimpleMath math = new SimpleMath(); } } }
SAO – Activator.GetObject namespace MathServer { class Program { static void Main(string[] args) { ChannelServices.RegisterChannel(new HttpChannel(20001), false); RemotingConfiguration.RegisterWellKnownServiceType(typeof(SimpleMath),"SimpleMath.soap",WellKnownObjectMode.SingleCall); } } } namespace MathKlient { class Program { static void Main(string[] args) {ISimpleMath math = (ISimpleMath)(Activator.GetObject(typeof(ISimpleMath), "http://localhost:20001/SimpleMath.soap")); } } }
Remoting gennem interface namespace IMathLibrary { public interfaceISimpleMath { int Add(int a, int b); int Sub(int a, int b); int Count {get;} } } namespace MathLibrary { public class SimpleMath : MarshalByRefObject, IMathLibrary.ISimpleMath private static int classCount = 0; private int instanceCount; public SimpleMath() {this.instanceCount = ++SimpleMath.classCount; } public int Add(int a, int b) {return a + b;} public int Sub(int a, int b) {return a - b;} public int Count { get { return this.instanceCount; } } } }