Portál AbcLinuxu, 19. listopadu 2025 21:15
Execute ve které se provádí určitá posloupnost, když byl v konstruktoru nastaven parametr threading na true provede se fork a v potomku se spustí vlákno pomocí pthread_create(&threadId, NULL, &runInThread, NULL). Funkce runInThread je deklarována v private jako static void * runInThread(void * arg) a definována následovně
void * Execute::runInThread(void * arg) {
while(1) {
if(!checking)
break;
cerr << "Tisknu o zivot" << endl;
}
return 0;
}
Problém je v testování proměnné checking což je privátní proměnná typu bool. Překladač zahlásíerror: invalid use of member ‘Execute::checking’ in static member function. Nevím jak do vlákna dostat mechanismus když se během vykonávání kódu ve třídě
Execute změní checking na false aby se vyskočilo ze smyčky a ukončilo i vlákno protože nemá smysl testování dále provádět.
runInThread je static, tak i proměnná checking musí být static (jinak se k ní statická metoda nedostane).
Další možností by asi bylo předávat proměnnou checking jako parametr vlákna (v ukazatelích se ještě moc nevyznám, něco jako pthread_create(&threadId, NULL, &runInThread, &checking)) a v metodě runInThread s ní pracovat přes arg, ale pak asi vzniká nebezpečí, že objekt obsahující checking může zaniknout dřív, než vlákno.
Diky moc za odpoved, zni to dobre ... vyzkousim
Hmm, a co třeba udělat členskou funkci v Execute? Něco jako metoda void Execute::setChecking(const bool check), která by se volala z rodičovského procesu, by nezabralo?
checking opravdu zastavit smyčku vlákna, proč to rovnou nedat do podmínky toho while místo toho, aby se to "přes ruku" přerušovalo breakem?
.
Jináč to vypadá, že to šlape
Definice vypadá takto :
void * Execute::runInThread(void * arg) {
bool* check = (bool *) arg;
while((*check)) {
cerr << "Tisknu o zivot" << endl;
sleep(1);
}
cerr << "Jdu si srknout Matecka" << endl;
return 0;
}
A vlákno spouštím takto pthread_create( &threadId, NULL, &runInThread, &checking);
checking tak, aby bylo zaručeno, že mi komplikátor nezoptimalizuje přístup k ní tak, že mi vlákno nikdy neskončí? Je volatile to správné řešení?
Nevím jak v C++, my v Javě máme u primitivních typů (s výjimkou longu a double) garantované atomické čtení i atomický zápis, takže tam opravdu stačí volatile (s Javovskou sémantikou, samozřejmě, která mimochodem zaručuje atomicitu čtení a zápisu i pro long a double).
To je poměrně častý omyl. Mutex samozřejmě potřebujete zamykat nejen v threadu, který zapisuje, ale i v těch, které čtou. Jinak stejně může ke kolizi dojít. Jediná situace, kdy zamykání není potřeba, je v případě, že se proměnná mění pouze ve vlákně, u kterého máte garantováno, že bude v daném okamžiku jediné, které může k proměnné (jakkoli) přistupovat - např. proto, že ostatní v tu chvíli ještě neexistují.
V tomto případě asi riziko nebude příliš velké, protože se jedná o typ, u něhož čtení i přiřazení obstará jediná instrukce, takže k problémům nejspíš nedojde. Ale stejně je lepší si na takové praktiky moc nezvykat.
In practice, you can assume that `int' is atomic. You can also assume that pointer types are atomic; that is very convenient. Both of these assumptions are true on all of the machines that the GNU C library supports and on all POSIX systems we know of.U složitějších typů by samozřejmě ten mutex potřeba byl, ale tady bych ho považoval za zbytečnou komplikaci.
IMHO ano, protože to je záležitost HW. ASM instrukce je stále tatáž a jedotlivé instrukce jsou na všech mě známých platformách nepřerušitelné.
Dokonce ani na x86 nejsou všechny operace pracující s pamětí defaultně atomické, a co teprv jinde.
). A čím dál víc mám dojem, že jde na jednu stranu o brutální diskrétní matematiku, a na druhou stranu o brutální low-level počítačové architektury. Je v silách běžného smrtelníka znát obojí?
To jsou jediní profesionální autoři kompilátorů, které osobně znám.
Totiž vlastně ještě jsem byl na pivu s Andreasem Jaegerem, ale ten je trochu z ruky. Ale s těma požadavkama máš docela pravdu...třeba se do toho tak za deset let zapojím taky.
Škoda, že měl naspěch.
int, o kterém už tu byla řeč.
1) chcípat vlákno přes booleovskou proměnnou bez dalších opatření (synch.primitiv) lze bez problému v případě, že zápis do proměnné je od přírody atomický a dál záleží na kontextu řízeného vlákna, zda mu nevadí, že třeba proběhne svoji smyčku o jednou méně nebo o jednou více. Což typicky nevadí u jednoduchých "hlídacích" smyček apod. Pokud vlákno čeká v nejakém blokujícím syscallu, který je navíc interruptible, tak se dá usmrcení vlákna vylepšit o to, že mu člověk navíc pošle signál - ale tady už bacha na atomicitu a synchronizaci... Pokud připadá v úvahu jak explicitní tak svévolné ukončení vlákna, začne být synchronizace na konci zajímavá
2) Pokud mám větší počet "dělnických" vláken stejného druhu, obvykle si pro ně vyrobím třídu worker_thr nebo tak nějak. V této třídě mám přesně v duchu původního příspěvku typicky nějakou booleovskou proměnnou typu "zhebni" nebo tak nějak
Ne statickou, ale normálního membera. Takže každé vlákno má svoji chcípací proměnnou. Plus jako normální membery mám v této třídě různá synchronizační primitiva (mutexy a podm.proměnné), podle potřeby. A samozřejmě jakákoli další data, která jsou thread-private pro dané dělnické vlákno. Potažmo pointer na instanci téhle třídy se dá poslat vláknu při startu skrz argument void* data v rámci pthread_create(), takže vlákno ví, kde má svůj vlastní soukromý objekt. Když to vezmete o krok dál, proč by vlastně funkce tvořící tělo vlákna nemohla být taky normální member metodou, takže by si svůj objekt nesla implicitně s sebou? Tedy proč ne: proto, že pointer na tuto funkci je třeba předat jako argument pthread_create(), což s normální member metodou nejde. Skrz legacy Cčkový prototyp pthread_create() neprotlačíte vedle pointeru na metodu ještě pointer na její její vlastnický objekt (nemluvě o problémech s late binding)... přesněji řečeno: pointer na metodu neuděláte. Ledaže by ta metoda byla deklarována jako statická. Pointer na statickou metodu udělat lze, a s použitím "plné kvalifikace" ji lze předat jako argument ptherad_create(worker_thr::makej). Jenom do ní musíte pointer na konkrétní objekt předat skrz tradiční void* data. Nakonec to dopadne tak, že skutečné tělo vlákna vymodelujete do podoby normální member metody (která může být i polymorfní), a k ní nadeklarujete ještě doprovodnou statickou metodu, která jí poslouží čistě jako tenký wrapper pro nahození vlákna skrz pthread_create().
Počítám, že by šlo nějak zapojit šablony nebo aspoň preprocesor, ušetřit si tak datlování a znepřehlednit zdroják
Taky si vybavuji, že pro C++ existují hotové "vláknovací knihovny", které se používají košer C++ způsobem... Výše popsané míchání C a sousedních primitivních rysů C++ zavání plebejstvím a polopatismem, které nesluší skutečnému mistru C++
Ale mě osobně tohle míchání Cčka s entry-level C++ vždycky náramně vyhovovalo... třeba kombinace C++ exceptions s posixovými vlákny a signály je vcelku hlavolam, pokud se pamatuji tak bez jednoznačného řešení.
Můžete mě nazvat zbabělcem, ale já bych POSIXové signály a thready nemíchal, dokud by to nebylo nezbytně nutné - každý z těchto fenoménů je dostatečná hrůza sám o sobě.
Jeden zvlášť děsivý příklad: Před dvěma lety jsem se potkal s programem, kde se podařilo pomocí kombinace volání fork(), clone() a pthread_create() vytvořit dva procesy, ve kterých funkce getpid() vracela stejnou hodnotu (!!). Nahlásil jsem to jako bug do glibc, a co se nestalo? Byl jsem odpálkován - prý to není žádný bug. POSIXová norma říká: po volání fork() může proces volat jenom takové funkce, které jsou "async-signal safe", jinak jsou výsledky nedefinované. A pthread_create() není "async-signal safe", takže se může stát cokoliv a je to úplně v pořádku. Kdyby po tomto volání počítač vyletěl do povětří, pravděpodobně by to bylo pořád OK.
Od té doby si na takové věci dávám pozor - člověk nikdy neví...
Můžete mě nazvat zbabělcem, ale já bych POSIXové signály a thready nemíchal, dokud by to nebylo nezbytně nutné - každý z těchto fenoménů je dostatečná hrůza sám o sobě.
No jo, ale co s threadem, který sedí v accept() a klidně by tam seděl ještě dva dny, než by se nějaký klient uráčil navázat spojení? Jedině použít pthread_cancel(), ale to taky není úplně bez adrenalinu…
Mě taky nenapadá nic jiného, než to přerušit nějakým signálem. Volání pthread_cancel() se bojím ještě víc než signálů, tam je těch pastiček jak naseto.
Jediná další možnost, která mě momentálně napadá, je použít neblokující volání accept() a volat ho pěkně ve smyčce s malým čekáním, aby nebylo na první pohled vidět, že je to busyloop. Což je ovšem ještě větší prasárna než všechny dosud zmíněné varianty.
POSIXová norma říká: po volánífork()může proces volat jenom takové funkce, které jsou "async-signal safe", jinak jsou výsledky nedefinované. Apthread_create()není "async-signal safe", takže se může stát cokoliv a je to úplně v pořádku.
Doufám, že se to týká jen rodiče.
Kdyby po tomto volání počítač vyletěl do povětří, pravděpodobně by to bylo pořád OK.
Z hlediska glibc asi ano, ale výrobci hardware by asi odvolání se na POSIX specifikaci jako výmluva nestačilo… :-)
fork() a exec(), to je jiná. Já už se lekl, že bych v potomkovi vzniklém fork() už nikdy nemohl zavolat pthread_create(). To by se ti multithreadoví démoni psali dost špatně…
Pokud vlákno čeká v nejakém blokujícím syscallu, který je navíc interruptible, tak se dá usmrcení vlákna vylepšit o to, že mu člověk navíc pošle signál Ha, milovník adrenalinových sportůNahodou, tady o zadny adrenalin nejde - protoze se (pravdepodobne - aspon ja bych to tak udelal) vubec nepouzije signal handler.
Tak nějak to frčí, ale ne na 100%. Nejsem vůbec odborník na procesy a už vůbec ne na vlákna, takže doufám, že moje komentáře nejsou moc z cesty.status=true a name=process1 tak se nejprve provede dynamické načtení třídy process1.so dále pokud je status=true tak se provede fork. V potomku se vytvoří vlákno, ve kterém se testuje(zmíněná proměnná checking, což je proměnná té dynamicky načtené třídy) a spustí se metoda execute() třídy process1, ta obsahuje většinou spouštění dalších externích příkázů ze shellu.Rodič má za úkol provést okamžitý tisk HTML odpovědi do prohlížeče, která obsahuje odkaz na HTML soubor, který obsahuje aktuální stav provádění a je aktualizován právě smyčkou ve vláknu.sleep(20) tak rodič neprovede tisk HTML odpovědi dřívě než za hodnotu, která je nastavena v sleep, tudíž, že potomek jako by neběžel samostatně.
Jinak moc děkuji, za vaše odpovědi a omlouvám se, jestli plácám blbosti a jsem úplně mimo, vím že není dobré to takhle plácat, čas je v tomto případě opravdu neůprosný
sleep z unistd.h a ne z pthread.h. Tímpádem sleep uspává celý proces a probouzí ho signálem. Ty ale chceš uspat jen vlákno.
Jen nevím, jak to, že ti to neřve něco o tom, že je metoda s identifikátorem sleep je definovaná dvakrát.
sleep(20) dávám v metodě execute() té dynamické třídy abych si ověřil, že zatímco se vykonává (v tomto případě spinká), rodič něco dělá a nespinká taky
. Opravdu chci aby se uspal proces, ale vlákno samozřejmě pracovalo dál a testovalo stav procesu skrz checking.
Tiskni
Sdílej:
ISSN 1214-1267, (c) 1999-2007 Stickfish s.r.o.