[C/Pthreads]Cancellen van threads die op mutex wachten

Pagina: 1
Acties:

  • ajvdvegt
  • Registratie: Maart 2000
  • Laatst online: 04-12-2025
Ik heb een programma dat meerdere threads gebruikt, maar dmv. mutexen wordt er voor gezorgt dat er maar 1 thread tegelijk actief is (dat klinkt onzinnig, dat weet ik, maar dat is niet onzinnig, alleen gaat het te ver om dat hier uit te leggen :Y)).
Threads staan dus vaak op een mutex te wachten, en als ik dan een thread die op een mutex wacht afsluit dmv pthread_cancel, en ik gebruik nadien die mutex weer voor een nieuwe thread, dan komt het systeem in een deadlock toestand (denk ik: 0% cpu gebruik, en m'n programma draait nog wel maar doet niks). Een versimpeld voorbeeld:

C++:
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
28
29
30
31
32
33
34
35
36
37
38
#include <pthread.h>
#include <iostream.h>
#include <stdlib.h>
#include <unistd.h>

using namespace std;

static pthread_mutex_t mutex;
static pthread_cond_t main_cond;
static pthread_cond_t task_cond;
static pthread_t task_thread;

static void* task_body(void* arg) {
  cout << "task: entern";
  cout << "task: mutex_lock=" << pthread_mutex_lock(&mutex) << endl;
  cout << "task: cond_signal(main)=" << pthread_cond_signal(&main_cond) << endl;
  cout << "task: cond_wait=" << pthread_cond_wait(&task_cond,&mutex) << endl;
  cout << "task: exitn";
  return 0;
}

int main() {
  cout << "main: enter" << endl;
  cout << "main: mutex_init=" << pthread_mutex_init(&mutex,0) << endl;
  cout << "main: mutex_lock=" << pthread_mutex_lock(&mutex) << endl;
  cout << "main: cond_init(main)=" << pthread_cond_init(&main_cond,0) << endl;
  cout << "main: cond_init(task)=" << pthread_cond_init(&task_cond,0) << endl;
//first thread creation:
  cout << "main: create=" << pthread_create(&task_thread,0,task_body,0) << endl;
  cout << "main: cond_wait=" << pthread_cond_wait(&main_cond,&mutex) << endl;
  cout << "main: cancel(task)=" << pthread_cancel(task_thread) << endl;

  cout << "main: mutex_lock=" << pthread_mutex_lock(&mutex) << endl;
//second thread creation:
  cout << "main: create=" << pthread_create(&task_thread,0,task_body,0) << endl;
  cout << "main: cond_wait=" << pthread_cond_wait(&main_cond,&mutex) << endl;
  cout << "main: exitn";
}


Als ik dit run is 'main: cancel(task)=0' van regel 34 het laatste wat er op het scherm verschijnt. De task (== thread) is wel gemaakt, dus het is de functie 'pthread_mutex_lock' die niet mee werkt.

Ik het dit getest op Redhat Linux 7.2 (met 2.4.20 kernel en g++ 2.96), Redhad 9 (met 2.4.2? kernel met NPTL en gcc 2.96), Knoppix 3.3 (Linux kernel 2.4.22 met g++ 3.2), en HPUX 11 (met aCC A.03.35), en die geven allen hetzefde resultaat. Als ik het echter op een Linux kernel 2.6.0test10 met g++ 3.2 probeer, dan werkt het wel! Ik weet dat deze nieuwe kernel NPTL heeft ipv. LinuxThreads, dus de hele implementatie is op nieuw gedaan.
Dus dat dit programma wel werkt met NPTL, wijst dat op een bug in NPTL of in en oude pthreads implementatie (wat dus ook fout zou zijn op HP Unix). Of is dat de interpretatie ruimte die de specs open laten?

Maar de hamvraag is eigenlijk: hoe krijg ik dit aan de praat op alle genoemde systemen? (al is RH7.2 de belangrijkste) Dingen als 'kernel en compiler upgraden' zijn geen optie, dit zijn bedrijfscomputers waar ik niet aan kan zitten: ik bedoel dus wijzigingen in de code 8).

[ Voor 12% gewijzigd door ajvdvegt op 04-12-2003 11:48 . Reden: Nu in kleur! ]

I don't kill flies, but I like to mess with their minds. I hold them above globes. They freak out and yell "Whooa, I'm *way* too high." -- Bruce Baum


  • Soultaker
  • Registratie: September 2000
  • Laatst online: 19:36
Het probleem is dat als je een thread canceled die locks vasthoudt, die daarna blijkbaar niet meer vrijgegeven worden. Een oplossing lijkt me om die hele thread_cancel niet te gebruiken maar op een andere manier (via een variabele per thread bijvoorbeeld) aan te geven dat de thread zichzelf moet beëindigen.

  • ajvdvegt
  • Registratie: Maart 2000
  • Laatst online: 04-12-2025
Ja, dat was ik even vergeten te melden: die work-around had ik ook verzonnen en wordt op dit moment geimplementeerd (we moeten het even snel aan de praat hebben :)). Maar is dit echt de oplossing?

Ohja, een andere oplossing is natuurlijk de threads helemaal niet cancelen meer gewoon laten bestaan terwil ze niets doen. Helaas was dat echter het uitgangspunt, en liep dat mis wanneer er meer dan 256 threads werden gebruikt (geen idee waar deze beperking vandaan komt trouwens). Vandaar dat de threads dus moeten worden opgeruimd.

I don't kill flies, but I like to mess with their minds. I hold them above globes. They freak out and yell "Whooa, I'm *way* too high." -- Bruce Baum


  • curry684
  • Registratie: Juni 2000
  • Laatst online: 12-05 22:23

curry684

left part of the evil twins

Hum ja, ik heb toevallig net een flinke discussie met John Reguera, core developer van DECThreads, over mutexes gehad, en de essentie van die discussie is dat je mutexes altijd netjes moet opruimen omdat het gedrag in 'undefined states' volstrekt onvoorspelbaar is en hoogstwaarschijnlijk lowlevel deadlocks veroorzaakt. Je synchronizatiemodel klopt dus niet.

edit:
DECThreads is trouwens de OpenVMS implementatie van pthreads.

[ Voor 10% gewijzigd door curry684 op 04-12-2003 15:17 ]

Professionele website nodig?


  • ajvdvegt
  • Registratie: Maart 2000
  • Laatst online: 04-12-2025
Nou, hehe, gevonden; voor de volledig heid maar even vermelden hier.

We hebben de workaround ingevoerd die elke thread een 'SELF-DESTRUCT' vlaggetjes geeft. Dit vlaggetje wordt door de mainthread gezet, en zodra een thread weer wordt geresumed na wachten op een mutex, kijkt-ie of hij er mee op moet houden.
Dit hielp ons echter nog niet van een ander probleem af: we konden niet meer dan 256 threads maken. Dat is lager dan het kernel limiet, dus er was nog een ander probleem.
Uiteindelijk hebben we in deze nieuwsgroep-posting
een soortgelijk probleem gevonden, en het blijkt dat je threads moet 'detach'-en, met 'pthread_detach':
C++:
1
cout << "main: detach=" << pthread_detach(task_thread) << endl;

direct na de pthread_create()'s.

Ook bedankt curry684, het gebruik van pthread_canel icm. met mutexen is dus geen goed idee? Dat staat helaas niet duidelijk in de man-pages (maar nu wel op GoT! ;))

I don't kill flies, but I like to mess with their minds. I hold them above globes. They freak out and yell "Whooa, I'm *way* too high." -- Bruce Baum


  • curry684
  • Registratie: Juni 2000
  • Laatst online: 12-05 22:23

curry684

left part of the evil twins

Netste manier om abortable threads te coden is gewoon met een volatile bool die Terminating aangeeft: die kun je van buitenaf veilig zetten en die check je dan in je main thread loop. Van buitenaf een thread forced neerhalen is nooit een goed plan. Onder Windows heb je de TerminateThread functie, die met dank aan strakke coding van de NT kernel in principe alle locked kernel objects (mutexes mainly) unlockt, maar je mag er nooit van uitgaan en die functie alleen als last resort gebruiken.

Wat jij dus aan het handje had is X threads die mutex A gebruiken, en je haalt een thread uit de lucht die die mutex nog vastheeft. Vervolgens staan er X-1 threads op die mutex te wachten die nooit vrijgegeven gaat worden. Dat is technisch geen echte deadlock, maar heeft wel dezelfde 'footprint' :)

Wat betreft die threadlimit, ik had 'm hier vandeweek ook aan het handje (alhoewel ik op VMS tot 3268 threads kon gaan 8)7 ) maar dat had meer van doen met dat ik de thread encapsulatie die we hier gebruiken iets slimmer had ingeschat ;)

Professionele website nodig?

Pagina: 1