c ++ - hvad er fordelen ved meddelelseskø over fælles data i trådkommunikation?

Indlæg af Hanne Mølgaard Plasc

Problem



Jeg læser en artikel om multithread programdesign http://drdobbs.com/architecture-and-design/215900465, det siger, at det er en god praksis, at 'erstatte fælles data med asynkrone meddelelser. Så meget som muligt, foretrækker du at holde hver tråds data isoleret (ikke delvist), og lad tråde i stedet kommunikere via asynkrone meddelelser, der overfører kopier af data. '[1]


Hvad der forvirrer mig er, at jeg ikke ser forskellen mellem at bruge fælles data og meddelelseskøer. Jeg arbejder nu på et ikke-gui-projekt på Windows, så lad os bruge Windows's meddelelseskøer og tage en tradition producent- forbruger problem som et eksempel.


Ved hjælp af delte data ville der være en delt container og en lås, der bevogter beholderen mellem producentens tråd og forbrugstråden. Når producentproduktproduktet først venter på låsen, og skriv derefter noget til beholderen, slip derefter låsen.


Ved hjælp af meddelelseskø, kunne producenten simpelthen PostThreadMessage uden blok. og dette er async-meddelelsens fordel, men jeg tror, ​​at der skal eksistere en lås, der beskytter meddelelseskøen mellem de to tråde, ellers vil dataene helt sikkert blive beskadiget. PostThreadMessage-opkaldet gemmer kun detaljerne. Jeg ved ikke, om jeg gætter Det er rigtigt, men hvis det er sandt, synes fordelen ikke længere at eksistere, da begge to metoder gør det samme, og den eneste forskel er, at systemet skjuler detaljerne, når du bruger meddelelseskøer.


ps. måske bruger meddelelseskøen en ikke-blokerende beholder, men jeg kunne også bruge en samtidig container på den tidligere måde. Jeg vil gerne vide, hvordan meddelelseskøen er implementeret, og er der nogen præstationsforskel mellem de to måder?


opdateret:
Jeg får stadig ikke begrebet async-besked, hvis meddelelsen kø-operationer stadig er blokeret et andet sted. Rett mig, hvis mit gæt var forkert: Når vi bruger delte containere og låse, blokerer vi i vores egen tråd. Men når du bruger meddelelsekøer , selv tråden kom straks tilbage, og forlod det blokerende arbejde til noget systemtråd.

Bedste reference


Besked passerer er nyttig til udveksling af mindre mængder data, fordi der ikke er behov for konflikter. Det er meget nemmere at implementere end fælles hukommelse til intercomputerkommunikation. Som du allerede har bemærket, har meddelelsespasning den fordel, at applikationsudviklere ikke behøver at bekymre sig om detaljerne i beskyttelse som delt hukommelse.


Delt hukommelse tillader maksimal hastighed og nem kommunikation, da det kan gøres ved hukommelseshastigheder, når det er inden for en computer. Delt hukommelse er normalt hurtigere end meddelelsesoverførsel, da meddelelsespasning typisk implementeres ved hjælp af systemopkald og dermed kræver de mere tidskrævende opgaver i kernelintervention . I modsætning hertil kræves systemopkald kun i fælleshukommelsessystemer for at etablere delte hukommelsesområder. Når først etableret er alle adgang behandlet som normale hukommelsesadgang uden ekstra assistance fra kernen.


Rediger : En sag, som du måske vil implementere din egen kø, er, at der er mange meddelelser, der skal produceres og forbruges, fx et loggesystem. Med implementeringen af ​​PostThreadMessage er køen kapacitet fastgjort. Meddelelser vil mest lide gå tabt, hvis denne kapacitet overskrides.

Andre referencer 1


Forestil dig at du har 1 trådproducerende data og 4 tråde, der behandler disse data (formodentlig at gøre brug af en multi core-maskine). Hvis du har en stor global pool af data, er du sandsynligvis nødt til at låse den, når nogen af trådene har brug for adgang, potentielt blokering af 3 andre tråde. Når du tilføjer flere behandlingstråde, øger du chancen for, at en lås skal vente og øge, hvor mange ting der skal vente. Til sidst tilføjer flere tråde ingenting, fordi alt du gør, er at bruge mere tid til at blokere.


Hvis du i stedet har en tråd, der sender beskeder til meddelelseskøer, en for hver forbruger tråd, så kan de ikke blokere hinanden. Du skal låse køen mellem producent og forbrugertråde, men da du har en separat kø for hver tråd, skal du Har en separat lås, og hver tråd kan ikke blokere alle de andre, der venter på data.


Hvis du pludselig får en 32-core-maskine, kan du tilføje 20 mere behandlingstråde (og køer) og forventer, at ydeevnen vil skale ret lineært i modsætning til det første tilfælde, hvor de nye tråde altid vil løbe ind i hinanden hele tiden.

Andre referencer 2


Jeg har brugt en delt hukommelsesmodel, hvor pegerne til den delte hukommelse styres i en meddelelseskø med forsigtig låsning. På en måde er det en hybrid mellem en meddelelseskø og delt hukommelse. Dette er meget, når store mængder data skal sendes mellem trådene, mens du beholder sikkerheden for meddelelseskøen.


Hele køen kan pakkes i en enkelt C ++ klasse med passende låsning og lignende. Nøglen er, at køen ejer det fælles lager og tager sig af låsningen. Producenter erhverver en lås for input til køen og modtager en peger til den næste ledige lagerplads (normalt en genstand af en slags), fylder den og frigiver den. Forbrugeren vil blokere, indtil den næste delte genstand er frigivet af producenten. Det kan så få en lås til lageret, behandle dataene og frigive det tilbage til poolen. I en passende designet kø kan du udføre flere producent/flere forbrugeroperationer med stor effektivitet. Tænk en Java-tråd sikker (java.util.concurrent.BlockingQueue) semantik men for peger til opbevaring.

Andre referencer 3


Selvfølgelig er der 'fælles data', når du sender meddelelser. Tværtimod er meddelelsen i sig selv en slags data. Den vigtige forskel er imidlertid, at når du sender en besked, vil forbrugeren modtage en kopi .



  PostThreadMessage-opkaldet gemmer bare detaljerne



Ja, det gør, men at være et WINAPI-opkald, kan du være rimeligt sikker på, at det gør det rigtigt.



  Jeg får stadig ikke begrebet async-besked, hvis meddelelsens køoperationer stadig er blokeret et andet sted.



fordel er mere sikkerhed. Du har en låsemekanisme, der systematisk håndhæves, når du sender en besked. Du behøver ikke engang at tænke over det, man kan ikke glemme at låse. I betragtning af at multi-thread bugs er nogle af de nastiest (tænk på raceforhold), er dette meget vigtigt. Besked passerer er et højere niveau af abstraktion bygget på låse.


ulempe er, at forbigående store mængder data sandsynligvis ville være langsomt. I så fald skal du bruge brug for delt hukommelse.


For at bestå status (dvs. arbejdstråd rapportering fremskridt til GUI) er meddelelserne vejen at gå.

Andre referencer 4


Det er ret simpelt (jeg er overrasket over, at andre skrev svar på sådanne længder!):


Ved hjælp af et meddelelseskø system i stedet for 'rå' delte data betyder, at du skal få synkroniseringen (låsning/oplåsning af ressourcer) lige kun én gang på et centralt sted.


Med et meddelelsesbaseret system kan du tænke højere i 'meddelelser' uden at skulle bekymre sig om synkroniseringsproblemer længere. For hvad det er værd, er det helt muligt, at en meddelelseskø implementeres ved hjælp af fælles data internt.

Andre referencer 5


Jeg tror, ​​at dette er nøgleelementet info der: 'Så meget som muligt, foretrækker du at holde hver tråds data isoleret (ufordelagt) og lad tråde i stedet kommunikere via asynkrone meddelelser, der overfører kopier af data'. Dvs. brug producent-forbruger :)

Du kan gøre din egen besked forbi eller bruge noget, der leveres af operativsystemet. Det er en implementeringsdetalje (skal gøres right ofc). Nøglen er at undgå fælles data, som at have samme område af hukommelse, der er ændret af flere tråde. Dette kan forårsage svære at finde fejl, og selvom koden er perfekt det vil spise ydeevne på grund af alle låse.

Andre referencer 6


Jeg havde præcis det samme spørgsmål. Efter at have læst svarene. Jeg føler:



  1. i de fleste typiske brugssager, kø=async, delt hukommelse (lås)=synkronisering. Faktisk kan du lave en async-version af delt hukommelse, men det er mere kode, der ligner genopfinning af meddelelsen, der passerer hjulet.

  2. Mindre kode=mindre fejl og mere tid til at fokusere på andre ting.



Fordele og ulemper er allerede nævnt af tidligere svar, så jeg gentager ikke.