[VC++|WinAPI] Hoe een thread+stack op te ruimen

Pagina: 1
Acties:

  • johnwoo
  • Registratie: Oktober 1999
  • Laatst online: 13-09 14:48
Ik ben aan een multithreaded FTP server bezig die als NT service draait. Werkt al prima (download beta) maar ik zit met een behoorlijk probleem: de server heeft een memory leak van anywhere tussen 10 en 40 KB per session.

Ik gebruikte maar 2 keer dynamisch gealloceerd buffers, en die worden allebei binnen dezelfde method ook weer netjes gefreed. Na enige uurtjes debuggen kwam ik erachter wat de oorzaak was: de WinAPI TerminateThread functie.

De sessions extenden een (door drZymo gemaakte) thread class die met CreateThread en TerminateThread werkt en een ThreadHandler method laat overriden. Ik moet zeggen dat we behoorlijk in onze nopjes waren met deze nette OOP oplossing :+ Maar ik kwam er dus achter dat TerminateThread een thread ook echt alleen maar terminate; alle lokale variabelen (de thread's stack dus) blijven gewoon in het geheugen, of je ze nu dynamisch of statisch alloceert.

Een flag zetten per thread waarmee je aan de thread laat weten dat hij zichzelf moet beëindigen is ook geen oplossing; de threads zullen de meeste tijd in een recv() call zitten, en het is de bedoeling dat een session per direct kan worden beeindigd, en niet pas als de client besluit weer wat te zenden. ExitThread zou eigenlijk een prima functie hiervoor zijn (deze ruimt namelijk wel de stack op), ware het niet dat deze alleen de aanroepende thread kan beeindigen...

Iemand enig idee hoe ik de complete thread, dus inclusief stack, opruim?

4200Wp ZO + 840Wp ZW + 1680Wp NW | 14xIQ7+ + 1xDS3-L | MTVenusE | HWP1


  • Dawai
  • Registratie: December 2000
  • Laatst online: 08-07 23:56

Dawai

HERiTAGE CHESS CREW

Op zondag 03 februari 2002 17:36 schreef johnwoo het volgende:
de threads zullen de meeste tijd in een recv() call zitten, en het is de bedoeling dat een session per direct kan worden beeindigd, en niet pas als de client besluit weer wat te zenden
Gebruik je blocking sockets dan :?

Programmer: red-eyed, mumbling mammal capable of conversing with inanimate objects.


  • johnwoo
  • Registratie: Oktober 1999
  • Laatst online: 13-09 14:48
Op zondag 03 februari 2002 17:50 schreef Dawai het volgende:

[..]

Gebruik je blocking threads dan :?
Ik gebruik blocking sockets ja :)

[edit] Hehe, snel veranderen he :P

4200Wp ZO + 840Wp ZW + 1680Wp NW | 14xIQ7+ + 1xDS3-L | MTVenusE | HWP1


  • Dawai
  • Registratie: December 2000
  • Laatst online: 08-07 23:56

Dawai

HERiTAGE CHESS CREW

Op zondag 03 februari 2002 17:52 schreef johnwoo het volgende:

Ik gebruik blocking sockets ja :)

[edit] Hehe, snel veranderen he :P
Foutje :)

Niet voor het een of ander hoor, maar is er een speciale reden waarom je blocking sockets gebruikt? Lijkt me niet zo clean...

Programmer: red-eyed, mumbling mammal capable of conversing with inanimate objects.


  • johnwoo
  • Registratie: Oktober 1999
  • Laatst online: 13-09 14:48
Op zondag 03 februari 2002 18:10 schreef Dawai het volgende:
Niet voor het een of ander hoor, maar is er een speciale reden waarom je blocking sockets gebruikt? Lijkt me niet zo clean...
Uhm ja, ik gebruik eigenlijk altijd blocking sockets :o waarschijnlijk omdat ik dat zo geleerd heb op school...
Ik weet dat je ook een window message kan laten sturen als er een winsock event optreedt, maar het probleem is dus: de server heeft geen windows (het is een service), dus een window message wordt een beetje moeilijk zonder window handle :o
Bovendien is het simpel en overzichtelijk; een nonblocking socket lijkt me juist veel lastiger te 'beheren'.

En wat het 'cleane' betreft; waarom is een blocking socket niet clean? Als er een error oprteedt of de verbinding wordt verbroken komt de call gewoon met die error terug hoor (en ja dat wordt ook afgevangen ;) ), t is niet zo dat ie dan eeuwig blijft hangen :)

4200Wp ZO + 840Wp ZW + 1680Wp NW | 14xIQ7+ + 1xDS3-L | MTVenusE | HWP1


  • drZymo
  • Registratie: Augustus 2000
  • Laatst online: 09-08 22:22
EN blocking sockets worden door alle OS'en gesupport :P

Maar dat terzijde. Het gaat erom dat na de CreateThread() 12k aan geheugen wordt gealloceerd. Nu heb ik wat tests gedaan met een simpele thread die gewoon nummers achter elkaar print.
code:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
DWORD WINAPI ThreadHandler(void*) {
    int i = 0;
    printf("Thread booted\n");
    while (i < 20) {
      printf("%i", i);
      Sleep(100);
      i++;
    }
    return 0;
}

HANDLE hThread;
DWORD dwThreadId;


int main() {
    Sleep(5000);
    hThread = CreateThread(NULL, NULL, ThreadHandler,
                     NULL, NULL, &dwThreadId);
    Sleep(5000);
    return 0;
}

Na inspectie met taskmanager kwam ik erachter dat na normale terminatie van de thread er nog steeds 4k overblijft in het geheugen. Is dit een foutje van CreateThread? Na verdere testen blijkt na abnormale terminatie (TerminateThread()) nog 4k meer overbleef, 8k totaal dus. En als dit bij iedere thread zo is dan is dat niet echt prettig. Weet iemand hoe dit komt? Want dan zijn we ook van die 12k memory leak af.

Een klein verloop van geheugen gebruik van het bovenstaande programma:
normale terminatie: 456k -> 468k -> 460k
brute terminatie: 456k -> 468k -> 464k

Iets wat niet helemaal klopt dus.

"There are three stages in scientific discovery: first, people deny that it is true; then they deny that it is important; finally they credit the wrong person."


Verwijderd

Op zondag 03 februari 2002 18:16 schreef johnwoo het volgende:

[..]
...maar het probleem is dus: de server heeft geen windows (het is een service), dus een window message wordt een beetje moeilijk zonder window handle :o
dit kan je oplossen door een "Message-Only Window" aantemaken.

zie http://msdn.microsoft.com/library/default.asp?url=/library/en-us/winui/windows_6u5v.asp

  • drZymo
  • Registratie: Augustus 2000
  • Laatst online: 09-08 22:22
Op zondag 03 februari 2002 20:48 schreef TechQwest het volgende:

[..]

dit kan je oplossen door een "Message-Only Window" aantemaken.

zie http://msdn.microsoft.com/library/default.asp?url=/library/en-us/winui/windows_6u5v.asp
Ja en tig KB meer geheugen moeten gebruiken voor de extra window zut en dergelijke? Blocked sockets werken prima alleen de threads sluiten niet.

Dusja nog steeds geen antwoord op ons probleem :P

"There are three stages in scientific discovery: first, people deny that it is true; then they deny that it is important; finally they credit the wrong person."


  • mbravenboer
  • Registratie: Januari 2000
  • Laatst online: 06-11-2025
Als je assembly wil/kan gebruiken kan je de stackpointer opslaan en aanpassen (8> .

Heb verder niet erg veel verstand van C++ dus weet niet of er een makkelijkere en betere oplossing is.

Blog, Stratego/XT: Program Transformation, SDF: Syntax Definition, Nix: Software Deployment


Verwijderd

Op zondag 03 februari 2002 22:59 schreef drZymo het volgende:
Ja en tig KB meer geheugen moeten gebruiken voor de extra window zut en dergelijke?
Tjah Tig KB zal wel mee vallen voor 'n onzichtbaar windowtje + windowhandler lijkt me, en wat heb je liever? een paar k extra nodig hebben of bij iedere thread 12 (of meer?) KB leaken?

Verwijderd

heb ff op msdn gezocht en daar kwam ik dit tegen

"The thread object remains in the system until the thread has terminated and all handles to it have been closed through a call to CloseHandle."

  • johnwoo
  • Registratie: Oktober 1999
  • Laatst online: 13-09 14:48
Op maandag 04 februari 2002 00:44 schreef TechQwest het volgende:
heb ff op msdn gezocht en daar kwam ik dit tegen

"The thread object remains in the system until the thread has terminated and all handles to it have been closed through a call to CloseHandle."
CloseHandle wordt ook gedaan, vlak na de TerminateThread, maar dat scheelt niets...

Maar kijk eens naar dat testproggel van drZymo: zelfs als je geen TerminateThread gebruikt (en geen hele app met sockets enzo) hou je 4K meer over dan waarmee je begon.. Zit toch ergens iets scheef, en aan dat simpele proggeltje lijkt me niks verkeerd, dus moet ik haast tot de conclusie komen dat die API call niet helemaal lekker werkt (of iig anders dan wij verwachten).

4200Wp ZO + 840Wp ZW + 1680Wp NW | 14xIQ7+ + 1xDS3-L | MTVenusE | HWP1


Verwijderd

Het terminaten van een thread met TerminateThread wordt in de msdn documentatie al sterk afgeraden, en er is een veel elegantere manier om dit soort dingen af te handelen.

De eerste eis is dat je gebruik maakt van non-blocking sockets, de 2e eis is dat je je een beetje verdiept in Windows' event mechanisme, en event sockets.

Het idee is dan dat je elke thread een met CreateEvent() gemaakt event object meegeeft en gebruik maakt van dingen als WSAEventSelect(). Vervolgens kan je met WSAWaitForMultipleEvents() kijken of er activiteit is op een van je sockets of dat je 'terminate event' gesignalled is. Als het om 't terminate event gaat spring je uit je main loop, ruim je je zooi netjes op en laat je de thread zichzelf afsluiten. In je hoofdprogramma kan je wachten tot een thread zichzelf heeft afgesloten door een WaitForSingleObject te doen op de thread's handle. (Bijvoorbeeld bij 't afluiten van je server)

  • curry684
  • Registratie: Juni 2000
  • Laatst online: 04-09 14:38

curry684

left part of the evil twins

Zowieso zul je inderdaad aan de non-blocking sockets moeten, maar dat is niet echt een probleem. Je moet dan inderdaad zoals Qlone zegt geen Window messages laten versturen maar via select (voor platform portability) of WSACreateEvent en co (voor Win32 optimized) liefst in een aparte thread (kan in main thread) pollen of er gelezen/geschreven kan worden.

TerminateThread is dodelijk. Het molesteert compleet de betreffende thread, geen destructors geen deallocations geen niets. Alles wat je dus gereserveerd hebt is lost forever (oftewel tot de volgende reboot). To quote MSDN:
TerminateThread is used to cause a thread to exit. When this occurs, the target thread has no chance to execute any user-mode code and its initial stack is not deallocated. DLLs attached to the thread are not notified that the thread is terminating.

TerminateThread is a dangerous function that should only be used in the most extreme cases. You should call TerminateThread only if you know exactly what the target thread is doing, and you control all of the code that the target thread could possibly be running at the time of the termination. For example, TerminateThread can result in the following problems:

• If the target thread owns a critical section, the critical section will not be released.
• If the target thread is allocating memory from the heap, the heap lock will not be released.
• If the target thread is executing certain kernel32 calls when it is terminated, the kernel32 state for the thread's process could be inconsistent.
• If the target thread is manipulating the global state of a shared DLL, the state of the DLL could be destroyed, affecting other users of the DLL.
Dit kun je elegant oplossen door middel van een externe notificatie, je maakt in je thread-encapsulatie een volatile bool aan die op false begint. Via een functie kun je deze op true zetten. In de main-functie van de thread doe je dan het volgende:
code:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
int MyThread::Execute()
{
...

while(!Terminating)
  {
  switch(select(...))     // Wacht bijv. 10ms op socket activiteit
    {
    case SOCKET_ERROR:    // Fout opgetreden (socket closed?)
    HandelFoutAf();
    return -1;     
    case 0:        // Geen activiteit
    break;
    default:          // Check socket(s) op activiteit
    DoeIetsMetSocket();
    break;
    }
  }
}

In de destructor van je thread class kun je met WaitForSingleObject wachten tot de thread klaar is (eventueel de Terminating bool naar true forcen) voordat je 'm met CloseHandle veilig wegknikkert.

Nog wat laatste hints:
• Gebruik _beginthreadex ipv CreateThread daar dat resourcevriendelijker is en gebruik van C/C++ standard libs toestaat.
• Zorg er in de destructor van je thread-class voor dat je niet infinite maar bijv. 60 seconden wacht totdat de thread dood is. Na 60 seconden kun je dan alsnog met TerminateThread (daar is ie voor bedoeld namelijk) de thread afschieten en een exception gooien dat er waarschijnlijk een deadlock opgetreden is.
• Zorg ervoor dat je altijd ergens een kernelmode wait doet in de main thread-loop!! De thread MOET om de tijd yielden om de rest van het programma niet te overbelasten cq. wel aan de beurt te laten (denk aan starvation). Doe tijdens langdurige operaties om de tijd een Sleep(0) om even te yielden, en tijdens idle tijden langere sleeps of gebruik events/mutexes/semaphores waitable timers voor wait states.

Ik denk dat je hier wel ff mee vooruit kunt... mocht je nog problemen hebben mail maar of ICQ (ik heb toevallig al wat libs geschreven met asynchrone socketencapsulatie in separate threads ;) )

[edit]
Code wat bijgeschoond.

Professionele website nodig?


  • drZymo
  • Registratie: Augustus 2000
  • Laatst online: 09-08 22:22
Op maandag 04 februari 2002 19:24 schreef curry684 genoeg goeie tips:
Thnx we zullen het een en ander hievan testen en laten het hier wel weer weten. Bedankt !

"There are three stages in scientific discovery: first, people deny that it is true; then they deny that it is important; finally they credit the wrong person."


  • drZymo
  • Registratie: Augustus 2000
  • Laatst online: 09-08 22:22
Even nog een klein puntje (en mischien een kleine herhaling). Dit keer NIET zeuren over blocking of geen blocking sockets want dat heeft hier HELEMAAL nix mee te maken. :P

Ik maak simpel een thread met CreateThread(), die ik gewoon laat afronden (return 0) door in de main() te wachten. Het zou gewoon prima moeten werken, maar mijn programma gebruikt voor het starten van de thread 4 KB minder als NA het afsluiten van de thread. Om een of andere reden wordt er iets van die thread niet weggehaald. En dan doe ik ook gewoon een CloseHandle naderhand. Kan iemand mij dus uitleggen WAT hier in godsnaam aan de hand is? :? Dit heeft dus even niks met sockets te maken.

De code die ik gebruikt heb voor het testen:
code:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
#include <stdio.h>
#include <windows.h>

DWORD WINAPI ThreadHandler(void*) {
    printf("Thread started.\n");
    printf("Waiting for 2 seconds...\n");
    Sleep(2000);
    printf("Thread finished.\n");
    return 0;
}

HANDLE hThread;
DWORD dwThreadId;

int main() {
    /* punt 1 */
    Sleep(5000);
    hThread = CreateThread(NULL, 0, ThreadHandler,
           NULL, NULL, &dwThreadId);
    /* punt 2 */
    Sleep(5000);
    if (CloseHandle(hThread) == 0)
      printf("CloseHandle() Error\n");
    /* punt 3 */
    Sleep(5000);
    return 0;
}

Errug basic code dus, maar het geheugen gebruik is dus wel zo:

punt 1 : 456 KB
punt 2 : 468 KB
punt 3 : 460 KB

Beetje vreemd niet?? :o

"There are three stages in scientific discovery: first, people deny that it is true; then they deny that it is important; finally they credit the wrong person."


Verwijderd

Neuh... da's niet zo vreemd. Windows' geheugenmanagement is ronduit bizar te noemen en er is een kansje dat ie dus 4k (1 page) reserveert bij het starten van de thread en die later niet meer vrijgeeft... Probeer eens 100x een thread te starten en kijk of je dan 400k kwijt bent. Zo niet, dan is 't windows in de bocht, zo wel, dan lek je (of een van je libraries) geheugen :)

Oh... nog een toevoeging. Curry684, je hoeft in windows als je correct gebruik maakt van het hele event systeem *nooit* (nooit dus, in grote knipperende letters met een rood randje en groene spikkels) te pollen voor dingen, en al helemaal niet voor zoiets basics als een stel sockets of wat threads...

En over terminatethread kan ik kort zijn: Als je die functie ooit nodig hebt heb je een programmeer- of denkfout gemaakt. Die functie hadden ze er beter niet in kunnen stoppen...

  • curry684
  • Registratie: Juni 2000
  • Laatst online: 04-09 14:38

curry684

left part of the evil twins

Geinig... ik zei dus dat je _beginthread moest gebruiken om memoryleaks bij gebruik van standard C-libs te voorkomen, en jij kijkt verbaasd op dat je leakt als je vervolgens CreateThread gebruikt en 3 keer een functie uit de standard C-lib aanroept in die thread :Y)

Hoeft niet de reden te zijn hoor, kan ook gewoon zijn dat er wat lingert. OS reserveert geheugen intern ook in blokken, en kan bijvoorbeeld best zijn dat ie de threadstack nog niet vrijgegeven heeft uit bepaalde overwegingen.

Memoryleaks van 4k hoef je je ook geen zorgen over te maken, dat zit meestal in het OS... jong die thread eens 500 keer (NIET TEGELIJK!!! *D ) en kijk dan naar het resultaat. Als je dan twee megabyte kwijt bent heb je wel een probleem.

Professionele website nodig?


Verwijderd

jong die thread eens 500 keer (NIET TEGELIJK!!! ) en kijk dan naar het resultaat
Dat zeg ik :) Gaar zo'n multiuser forum :)

  • curry684
  • Registratie: Juni 2000
  • Laatst online: 04-09 14:38

curry684

left part of the evil twins

Op maandag 04 februari 2002 23:54 schreef Qlone het volgende:
Oh... nog een toevoeging. Curry684, je hoeft in windows als je correct gebruik maakt van het hele event systeem *nooit* (nooit dus, in grote knipperende letters met een rood randje en groene spikkels) te pollen voor dingen, en al helemaal niet voor zoiets basics als een stel sockets of wat threads...
Jaja ik ben niet achterlijk of zo. Een platformonafhankelijke implementatie van sockets vereist echter dat je via de Berkeleyimplementaties werkt, en async Berkeley gaat nu eenmaal via select. En aan select is niets mis, je kunt er een timeout bij opgeven en het ding verkeert in kernelmode wait zolang die timeout niet verstreken is. Maar in essentie vraag je met select wel de status op van de gegeven sockets, oftewel je pollt naar hun status. Niet gaan mierenneuken over een woord aub.
En over terminatethread kan ik kort zijn: Als je die functie ooit nodig hebt heb je een programmeer- of denkfout gemaakt. Die functie hadden ze er beter niet in kunnen stoppen...
Er moet een mogelijkheid zijn om rogue threads af te kappen. Stel dat er iets vastloopt in die FTP-server: volgens jouw theorie moet dat ding dan maar systemresources blijven vreten tot de volgende reboot. Dan beter een mechanisme dat de rogue thread kan afschieten met een veel kleinere resourceleak en een fatsoenlijke waarschuwing kan afgeven.

Volgens jouw theorie mag je ook geen rogue processes afschieten via Task Manager, da's dus helemaal je computer rebooten iedere keer dat er een programma vastloopt.

Professionele website nodig?


Verwijderd

Jaja ik ben niet achterlijk of zo.
Ok ok... was niet aanvallend bedoeld ofzo. Vat 't niet persoonlijk op ofzo!
Een platformonafhankelijke implementatie van sockets vereist echter dat je via de Berkeleyimplementaties werkt, en async Berkeley gaat nu eenmaal via select.
Maar dit is een [VC++|WinAPI] vraag volgens 't topic, dus waarom zou je dan gebruik maken van een berkeley API als je de (naar mijn idee) iets mooiere API van MS kan gebruiken :)
Een platformonafhankelijke implementatie van sockets vereist echter dat je via de Berkeleyimplementaties werkt, en async Berkeley gaat nu eenmaal via select. En aan select is niets mis, je kunt er een timeout bij opgeven en het ding verkeert in kernelmode wait zolang die timeout niet verstreken is.
Het nadeel van select echter, is dat je *alleen* kunt wachten op activiteit op een socket. Het grote voordeel van het gebruik van de Windows API is dat je ook kan wachten op andere events, zoals bijvoorbeeld een verzoek tot afsluiten van je thread. Op deze manier loopt je proces niet rondjes te rennen en telkens te kijken of er toevallig nog wat anders is gebeurd dan verkeer op een socket. Tevens reageert je thread daardoor dus OOK sneller op andere events dan alleen socketverkeer. (Anders wordt er pas gekeken na de timeout... die kan je lang nemen, dan spaar je CPU tijd, of die kan je kort nemen, dan reageert e.e.a. sneller)
Er moet een mogelijkheid zijn om rogue threads af te kappen. Stel dat er iets vastloopt in die FTP-server:
Als er iets vastloopt in die FTP server heb je dus een programmeer- of denkfout gemaakt. Je geeft nu een reden waarom 't makkelijk zou kunnen zijn threads af te schieten (en, ik geef toe, tijdens debuggen is 't ideaal) maar in software van een beetje redelijke kwaliteit mag zoiets gewoon niet voorkomen.

Software maken die foutcondities redelijk afhandelt is een vak apart. Het is natuurlijk belangrijk dat een stuk software eventuele fouten kan herkennen en er zinnig mee om kan gaan, maar nog veel belangrijker is het om fouten te voorkomen.
Volgens jouw theorie mag je ook geen rogue processes afschieten via Task Manager, da's dus helemaal je computer rebooten iedere keer dat er een programma vastloopt.
Dat zeg ik niet. Nu overdrijf je m'n stelling weer. Natuurlijk mag je op hol geslagen processen afschieten. Het verschil hier is alleen dat je 't dan *zelf* doet, in plaats van dat een proces besluit 'hee... kijk nou. ik ben op hol geslagen! Laat ik eens wat threads afschieten op een zeer ranzige manier waarvan ik niet weet hoe 't systeem er op reageert'. Als ik 't zelf doe weet ik wat ik kan verwachten.

  • curry684
  • Registratie: Juni 2000
  • Laatst online: 04-09 14:38

curry684

left part of the evil twins

Op dinsdag 05 februari 2002 00:29 schreef Qlone het volgende:
Maar dit is een [VC++|WinAPI] vraag volgens 't topic, dus waarom zou je dan gebruik maken van een berkeley API als je de (naar mijn idee) iets mooiere API van MS kan gebruiken :)
Ik vind zelf de Berkeley API mooier, maar da's natuurlijk persoonlijk. Vooral draken als de WSAPROTOCOL_INFO struct e.d. maken het erg oncharmant coden tegenover de simpliciteit van Berkeley. En het is portable :Y)
<...knip...>
Het grote voordeel van het gebruik van de Windows API is dat je ook kan wachten op andere events, zoals bijvoorbeeld een verzoek tot afsluiten van je thread.
<...knip...>
Ware het niet dat ik zo snel nergens in de docs kan vinden dat je WSAEVENT handles en reguliere event-handles door mekaar mag gebruiken. Lijkt me ook wazig dat ze specifiek aparte functies maken als WSAWaitForMultipleEvents die in essentie omwegen zijn naar WaitForMultipleObjects. Indien het wel zo is, my bad, dan heb je 100% gelijk. Ik heb sockets altijd via Berkeley gemaakt :)
Dat zeg ik niet. Nu overdrijf je m'n stelling weer.
Ik reageerde vooral op de nogal boute conclusie onderaan je stukje dat de TerminateThread functie beter nooit gemaakt had kunnen worden, wat ik als onzin betitelde. Hij is wel degelijk nuttig in hele specifieke geisoleerde situaties.

Professionele website nodig?


  • curry684
  • Registratie: Juni 2000
  • Laatst online: 04-09 14:38

curry684

left part of the evil twins

Een kleine toevoeging:
Op dinsdag 05 februari 2002 00:29 schreef Qlone het volgende:
Het nadeel van select echter, is dat je *alleen* kunt wachten op activiteit op een socket. Het grote voordeel van het gebruik van de Windows API is dat je ook kan wachten op andere events, zoals bijvoorbeeld een verzoek tot afsluiten van je thread. Op deze manier loopt je proces niet rondjes te rennen en telkens te kijken of er toevallig nog wat anders is gebeurd dan verkeer op een socket. Tevens reageert je thread daardoor dus OOK sneller op andere events dan alleen socketverkeer. (Anders wordt er pas gekeken na de timeout... die kan je lang nemen, dan spaar je CPU tijd, of die kan je kort nemen, dan reageert e.e.a. sneller)
Als je vanuit de roepende thread de socket handle sluit valt die select meteen terug, wat een geaccepteerde methode is om dit doel te bereiken, en immediate is. Daarnaast meldt de documentatie ook netjes dat het altijd verstandiger is om de lopende socketcommunicatie fatsoenlijk af te laten lopen door eerst via shutdown het zenden af te stoppen en daarna de laatste pending data te receiven.

Overigens werkt die handle-close methode ook om blocking calls te beeindigen (hij hoeft dus niet perse async, om weer wat on-topic te komen ;) ).

Professionele website nodig?


  • curry684
  • Registratie: Juni 2000
  • Laatst online: 04-09 14:38

curry684

left part of the evil twins

Op zondag 03 februari 2002 17:36 schreef johnwoo het volgende:
Ik ben aan een multithreaded FTP server bezig die als NT service draait. Werkt al prima (download beta) maar ik zit met een behoorlijk probleem: de server heeft een memory leak van anywhere tussen 10 en 40 KB per session.
Je hebt trouwens sowieso hiernaast een enorm performanceleak: je maakt voor iedere connectie een thread aan, wat best wel op je performance inhakt als dat ding dadelijk 100 connecties per minuut gaat krijgen (constant draadje maken, stack reserveren, inschedulen etc. en dan weer alles afbreken is *niet* snel). Ga eens snel een mooie effectieve threadpool maken voor dat ding :9

Professionele website nodig?


  • curry684
  • Registratie: Juni 2000
  • Laatst online: 04-09 14:38

curry684

left part of the evil twins

Even een ordinaire schup uit pure interesse: is het ondertussen al allemaal netjes gelukt, en hoe?

Professionele website nodig?


Verwijderd

Op dinsdag 05 februari 2002 02:30 schreef curry684 het volgende:

[..]

Je hebt trouwens sowieso hiernaast een enorm performanceleak: je maakt voor iedere connectie een thread aan, wat best wel op je performance inhakt als dat ding dadelijk 100 connecties per minuut gaat krijgen (constant draadje maken, stack reserveren, inschedulen etc. en dan weer alles afbreken is *niet* snel). Ga eens snel een mooie effectieve threadpool maken voor dat ding :9
Die performance leak zit em niet zozeer in telkens een thread maken en afsluiten maar meer in het feit dat je op je server een thread voor elke connectie nodig hebt...Op die manier zul je niet meer dan een stuk of 20 users tegelijk kunnen serven op een beetje een normale machine omdat windows dan alleen nog maar bezig is met het switchen tussen threads, wat dus niet erg opschiet...Check MSDN eens op IOCompletion ports, dit is dus hoe de 'echte' programma's het doen (IIS). Enige nadeel is dat het alleen werkt op >=win2k, maar ja, je wil toch geen server draaien op win9x...
Succes!

  • curry684
  • Registratie: Juni 2000
  • Laatst online: 04-09 14:38

curry684

left part of the evil twins

Op zaterdag 02 maart 2002 23:40 schreef hondass50 het volgende:
Die performance leak zit em niet zozeer in telkens een thread maken en afsluiten maar meer in het feit dat je op je server een thread voor elke connectie nodig hebt...Op die manier zul je niet meer dan een stuk of 20 users tegelijk kunnen serven op een beetje een normale machine omdat windows dan alleen nog maar bezig is met het switchen tussen threads, wat dus niet erg opschiet...Check MSDN eens op IOCompletion ports, dit is dus hoe de 'echte' programma's het doen (IIS). Enige nadeel is dat het alleen werkt op >=win2k, maar ja, je wil toch geen server draaien op win9x...
Succes!
WinNT/2000/XP kan dan ook in tegenstelling tot Win9x wel probleemloos tegen 20+ threads per process overigens... :z

Maar je hebt wel gelijk voor het echte grote werk.

edit:

Ter illustratie werp ik hier even onder een non-server XP op een rustig moment een blik in Task Manager:
• svchost.exe 79 threads
• System 50 threads
• inetinfo.exe 30 threads
• sqlservr.exe 20 threads
• winlogon.exe 20 threads
• services.exe 18 threads

Professionele website nodig?


Verwijderd

Op zondag 03 maart 2002 17:48 schreef curry684 het volgende:

WinNT/2000/XP kan dan ook in tegenstelling tot Win9x wel probleemloos tegen 20+ threads per process overigens... :z

Maar je hebt wel gelijk voor het echte grote werk.

edit:

Ter illustratie werp ik hier even onder een non-server XP op een rustig moment een blik in Task Manager:
• svchost.exe 79 threads
• System 50 threads
• inetinfo.exe 30 threads
• sqlservr.exe 20 threads
• winlogon.exe 20 threads
• services.exe 18 threads
Wow, k zit hier nu ook even op mn compu te kijken met win2k en dat zijn idd best wat threads voor sommige proggies, maar dit geeft natuurlijk geen echt goed beeld aangezien het overgrote deel van deze threads gewoon een beetje niets zit te doen en te wachten op events, op die manier hoeven ze natuurlijk ook nauwelijks gescheduled te worden waardoor het ook nauwelijks performance hoeft te kosten... Ik heb gisteren toevallig een testje gedaan op een PII 333 met win2k met IOCompletion ports + sockets en daar zag ik toch echt heel duidelijk dat met 20 actieve threads (dus ook >20 connecties) de boel echt veeeeel trager ging dan met slechts een stuk of 4 threads voor hetzelfde aantal connecties. Lijkt op het eerste gezicht misschien een beetje vreemd om slechts zo weinig threads te gebruiken, maar als je er even over nadenkt is het toch eigenlijk wel weer logisch. Maar nogmaals, je hebt natuurlijk wel gelijk als je zegt dat een hoop treads op zich niet zo'n probleem is, maar een hoop zeer actieve threads zijn dus wel degelijk een probleem.

  • curry684
  • Registratie: Juni 2000
  • Laatst online: 04-09 14:38

curry684

left part of the evil twins

Op zondag 03 maart 2002 21:53 schreef hondass50 het volgende:
Wow, k zit hier nu ook even op mn compu te kijken met win2k en dat zijn idd best wat threads voor sommige proggies, maar dit geeft natuurlijk geen echt goed beeld aangezien het overgrote deel van deze threads gewoon een beetje niets zit te doen en te wachten op events, op die manier hoeven ze natuurlijk ook nauwelijks gescheduled te worden waardoor het ook nauwelijks performance hoeft te kosten...
Waarmee je je eigen opmerking tot onzin reduceert... of je thread nu in kernelmode op een WSAEvent staat te wachten of dat het systeem een usermode APC scheduled voor je completion routine is volgens mij redelijk dezelfde overhead. Pas bij honderden threads gaan de APC's denk ik echt in je voordeel werken...
Ik heb gisteren toevallig een testje gedaan op een PII 333 met win2k met IOCompletion ports + sockets en daar zag ik toch echt heel duidelijk dat met 20 actieve threads (dus ook >20 connecties) de boel echt veeeeel trager ging dan met slechts een stuk of 4 threads voor hetzelfde aantal connecties.
Maar zit je op die 20 threads dan te pollen met sleeps indien je tijdelijk niets hebt of gebruik je dan kernelmode waits met overlapped operaties? :?
Maar nogmaals, je hebt natuurlijk wel gelijk als je zegt dat een hoop treads op zich niet zo'n probleem is, maar een hoop zeer actieve threads zijn dus wel degelijk een probleem.
Het lijkt me ook sterk dat er op een FTP-server ooit alle threads tegelijkertijd druk willen zijn. En het lijkt me evenzeer sterk dat je dan veel meer vertraging krijgt dan in het geval van dat de kernel op dat moment 100 APC's in je thread-queue schuift. Is een benchje waard overigens... 8-)

Professionele website nodig?


Verwijderd

Op zondag 03 maart 2002 23:41 schreef curry684 het volgende:

[..]

Waarmee je je eigen opmerking tot onzin reduceert... of je thread nu in kernelmode op een WSAEvent staat te wachten of dat het systeem een usermode APC scheduled voor je completion routine is volgens mij redelijk dezelfde overhead. Pas bij honderden threads gaan de APC's denk ik echt in je voordeel werken...
Zit wat in ware het niet dat ik dus uit ga van actieve threads en de problemen daarvan, niet het feit dat veel threads op zich vertraging veroorzaken.
Maar zit je op die 20 threads dan te pollen met sleeps indien je tijdelijk niets hebt of gebruik je dan kernelmode waits met overlapped operaties?
Uiteraard geen polling + sleeps, maar overlapped operaties. Dit is namelijk precies waar IOCompletionPorts voor bedoeld zijn.
Het lijkt me ook sterk dat er op een FTP-server ooit alle threads tegelijkertijd druk willen zijn. En het lijkt me evenzeer sterk dat je dan veel meer vertraging krijgt dan in het geval van dat de kernel op dat moment 100 APC's in je thread-queue schuift. Is een benchje waard overigens...
Tja, tuurlijk zullen op een FTP sessie niet alle threads tegelijk druk zijn, maar slechts een bepaald percentage gemiddeld genomen. Maar daarmee veranderd het probleem niet echt, feit is dat mijn testjes in ieder geval voor mij hebben aangetoond dat deze extreme vertraging toch al behoorlijk snel optreden.
Daar kwam nog eens bij dat mijn test server extreem simpel was, namelijk de string die door de client wordt gestuurd weer terugsturen. Hoewel dit natuurlijk een extreem simpele operatie is zie je toch al dat als ik 20 clients run die allemaal zo snel mogelijk proberen te connecten,een string te sturen, op antwoord wachten en disconnecten en dat in een flinke loop dat dit met 20 threads + 20 zeer actieve clients onevenredig veel overhead opleverd aan threadscheduling. Het is dan ook niet voor niets dat er bij die completion ports standaard slechts hetzelfde aantal actieve threads (die IO uitvoeren dan he) als het aantal processors in de machine wordt toegestaan!

Maar goed, om weer even terug te komen op het FTP proggie, of het daarvoor nuttig is of niet hangt natuurlijk helemaal of van het gebruik en de schaalbaarheid die hij moet hebben. Aangezien het gebruikt van die completionports echt niet veel ingewikkelder is dan het gewone implementeren van sockets met een thread per sessie denk ik eigenlijk dat er weinig redenen zijn om het niet gewoon in een keer goed te doen...de enige reden zou wellicht win9x support kunnen zijn, maar goed, dat is mijn mening...
Maareh, Curry, als jij andere benchmarks ergens vandaan weet te toveren die het tegendeel bewijzen ben ik natuurlijk zeer geinteresseerd!

Verwijderd

In de linux kernel docu heb ik ooit eens gelezen dat een process altijd een minimale hoeveelheid processortijd (onafhankelijk van het aantal processen) krijgt voordat er getaskswitched wordt. Bij veel processen zorgt dat ervoor dat je processortijd niet alleen voor het schedulen gebruikt wordt.

Ik neem aan dat iets dergelijks ook wel bij windows het geval zal zijn bij het schedulen van threads?

  • curry684
  • Registratie: Juni 2000
  • Laatst online: 04-09 14:38

curry684

left part of the evil twins

Op maandag 04 maart 2002 10:33 schreef lnfinitive het volgende:
In de linux kernel docu heb ik ooit eens gelezen dat een process altijd een minimale hoeveelheid processortijd (onafhankelijk van het aantal processen) krijgt voordat er getaskswitched wordt. Bij veel processen zorgt dat ervoor dat je processortijd niet alleen voor het schedulen gebruikt wordt.

Ik neem aan dat iets dergelijks ook wel bij windows het geval zal zijn bij het schedulen van threads?
Wat je hier noemt gaat om iets anders: het voorkomen van starvation. Als je die gegarandeerde minimum tijd namelijk niet afgeeft is het theoretisch mogelijk dat een process dat nooit yield (rogue of bedoeld) de rest van het systeem dicht kan timmeren. Stel dat je een compiler draait op een klusje van een uur, en de compile-thread yield nooit. Daar het een belangrijke klus is zet je de process priority op 'High' zodat er niet een of andere kuttig process een hoop vertraging op kan leveren. Explorer.exe draait echter op 'Normal'.

Zonder starvation protection zou bovenstaand voorbeeld een uur lang je computer geen enkele repaint of muisbeweging toestaan... vandaar dat onder NT-kernels dit er wel in zit.

Professionele website nodig?

Pagina: 1