Google Groups no longer supports new Usenet posts or subscriptions. Historical content remains viewable.
Dismiss

Bedingungsvariablen blockieren

70 views
Skip to first unread message

Rainer Grimm

unread,
May 6, 2012, 4:57:45 AM5/6/12
to
Hallo zusammen,
ich habe das folgende Programm geschrieben, das einen durch Bedingungsvariablen synchronisierten Arbeitsablauf darstellt.
Dabei geben zwei Arbeiter immer Bescheid (notify_one) , wann sie ihre Arbeit erledigt haben.
Ist dies getan, signalisiert ihnen der Boss (notify_all), dass sie weitermachen können.
Diese Interaktion geschieht zweimal.
Um sicher zu gehen, dass der Boss die richtige Anzahl an Benachrichtungen erhält, verwende ich in dem Programm eine atomare Variable.

#include <atomic>
#include <chrono>
#include <condition_variable>
#include <iostream>
#include <mutex>
#include <random>
#include <thread>


// Boss -> Worker
bool startWork, goHome;

std::mutex startWorkMutex, goHomeMutex;

std::condition_variable boss2WorkerCondVariable;

// Worker -> Boss
std::mutex preparedMutex, doneMutex;

std::condition_variable worker2BossCondVariable;

std::atomic_int preparedCount, doneCount;


int getRandomTime(int start, int end){

std::random_device seed;
std::mt19937 engine(seed());
std::uniform_int_distribution<int> dist(start,end);

return dist(engine);
};

class Worker{
public:
Worker(std::string n):name(n){};

void operator() (){
// prepare the work and notfiy the boss
int prepareTime= getRandomTime(500,2000);
std::this_thread::sleep_for(std::chrono::milliseconds(prepareTime));
preparedCount++;
std::cout << name << ": " << prepareTime << std::endl;
worker2BossCondVariable.notify_one();

// { ist wohl notwendig
// wait for the start notification of the boss
std::unique_lock<std::mutex> startWorkLock( startWorkMutex );
boss2WorkerCondVariable.wait( startWorkLock,[]{ return startWork;});
// }

// do the work and notify the boss
int workTime= getRandomTime(200,400);
std::this_thread::sleep_for(std::chrono::milliseconds(workTime));
doneCount++;
std::cout << name << ": " << workTime << std::endl;
worker2BossCondVariable.notify_one();

// { ist wohl notwendig
// wait for the go home notification of the boss
std::unique_lock<std::mutex> goHomeLock( goHomeMutex );
boss2WorkerCondVariable.wait( goHomeLock,[]{ return goHome;});
// }

}
private:
std::string name;
};

int main(){

Worker worker1(" Worker1");
std::thread worker1Work(worker1);

Worker worker2(" Worker2");
std::thread worker2Work(worker2);

std::cout << "BOSS: PREPARE YOUR WORK.\n " << std::endl;

// waiting for the worker
preparedCount.store(0);
std::unique_lock<std::mutex> preparedUniqueLock( preparedMutex );
worker2BossCondVariable.wait(preparedUniqueLock,[]{ return preparedCount == 2; });

// notify the worker about the begin of the work
startWork= true;
std::cout << "\nBOSS: START YOUR WORK. \n" << std::endl;
boss2WorkerCondVariable.notify_all();

// waiting for the worker
doneCount.store(0);
std::unique_lock<std::mutex> doneUniqueLock( doneMutex );
worker2BossCondVariable.wait(doneUniqueLock,[]{ return doneCount == 2; });

// notify the worker about the end of the work
goHome= true;
std::cout << "\nBOSS: GO HOME. \n" << std::endl;
boss2WorkerCondVariable.notify_all();

worker1Work.join();
worker2Work.join();

}

Führe ich das Programm aus, blockiert es.

BOSS: PREPARE YOUR WORK.

Worker1: 658
Worker2: 1836

BOSS: START YOUR WORK.

Worker1: 270
^C

Kommentiere ich den künstlichen Bereich im Funktor (siehe Sourcecode) wieder ein, verhält sich das Programm erwartungsgemäß.

BOSS: PREPARE YOUR WORK.

Worker1: 650
Worker2: 1507

BOSS: START YOUR WORK.

Worker1: 201
Worker2: 364

BOSS: GO HOME.

Das beobachtete Verhalten erhalte ich sowohl mit dem GCC4.6 als auch GCC4.7.
Ich sehe keinen Zusammenhang zwischen der Lebenszeit des unique_lock im Körper des Funktors und dem blockieren der Threads.


Viele Grüße,
Rainer Grimm

Rainer Grimm

unread,
May 8, 2012, 3:43:30 PM5/8/12
to
Hallo,
in der Hoffnung, eine Erklärung für das unerwartete Blockieren der Bedinungsvariablen zu erhalten, habe ich das Problem noch auf comp.lang.c++.moderated gepostet.

Viele Grüße,
Rainer Grimm

Stefan Reuther

unread,
May 10, 2012, 1:00:06 PM5/10/12
to
Rainer Grimm wrote:
> Hallo zusammen,
> ich habe das folgende Programm geschrieben, das einen durch
> Bedingungsvariablen synchronisierten Arbeitsablauf darstellt.
> Dabei geben zwei Arbeiter immer Bescheid (notify_one) , wann
> sie ihre Arbeit erledigt haben.
> Ist dies getan, signalisiert ihnen der Boss (notify_all),
> dass sie weitermachen können.
> Diese Interaktion geschieht zweimal.

Mir ist jetzt nicht ganz klar, welches Problem du wirklich lösen willst,
aber ich löse Boss/Worker-Probleme ja immer mit Semaphoren statt
Bedingungsvariablen.

Neues Stück Arbeit? In (mutexgeschützte) Queue packen und Semaphore
'newWork' lupfen. Ein Worker, der gerade frei ist, greift es sich.
Alternativ feste Zuteilung mit einer Queue pro Worker.

Stück Arbeit fertig? In (mutexgeschützte) Queue packen und Semaphore
'workDone' lupfen.

Probleme mit mehrfachen oder verlorenen Signalisierungen sind mir damit
noch nicht untergekommen. Da ich allerdings noch in C++03 unterwegs bin,
kann ich jetzt nicht mit Beispielcode dienen.


Stefan

Rainer Grimm

unread,
May 11, 2012, 4:59:56 PM5/11/12
to
Hallo Stefan
> Mir ist jetzt nicht ganz klar, welches Problem du wirklich lösen willst,
> aber ich löse Boss/Worker-Probleme ja immer mit Semaphoren statt
> Bedingungsvariablen.
ich will kein Problem lösen. Ich wollte einfach einen komplexeren Arbeitsablauf
über Bedingungsvariablen synchonisieren. Dabei sollten die wait-Aufrufe wie
Barrieren wirken. Alle Threads haben solange zu warten bis der Boss sein Go gibt. Tatsächlich ist ein komplexer Arbeitsablauf mit Bedingungsvariablen
deutlich schwieriger zu erreichen als ich anfänglich annahm.
>
> Neues Stück Arbeit? In (mutexgeschützte) Queue packen und Semaphore
> 'newWork' lupfen. Ein Worker, der gerade frei ist, greift es sich.
> Alternativ feste Zuteilung mit einer Queue pro Worker.
>
> Stück Arbeit fertig? In (mutexgeschützte) Queue packen und Semaphore
> 'workDone' lupfen.
>
> Probleme mit mehrfachen oder verlorenen Signalisierungen sind mir damit
> noch nicht untergekommen.
Du hast Recht. Verloren gegangene Signale halte ich für ein größte Gefahrenquelle bei Bedingungsvariablen. Dazu kommen noch die seltsamen "spurious wakeups".
> Da ich allerdings noch in C++03 unterwegs bin,kann ich jetzt nicht mit
Beispielcode dienen.
Das ist meine Grundannahme. Ich will C++11 verwenden. Da gibt es keine Semaphoren. Ich halte mittlerweile futures mit ihren wait-Aufrufen für die deutlich einfachere Möglichkeit, Arbeitsabläufe mit Threads zu synchronisieren.
Der Grund für das Blockieren meines Arbeitablaufes war sehr simpel. Der durch
den unique_lock im Körper des Funktors gebunden mutex habe ich vergessen freizu-
geben. Somit erhielten ihn der andere Worker nicht mehr.
Genaueres könnt ihr in comp.lang.c++.moderated nachlesen.

Gruß,
Rainer

Stefan Reuther

unread,
May 12, 2012, 12:24:27 PM5/12/12
to
Hallo,

Rainer Grimm wrote:
>>[Semaphore]
>>Probleme mit mehrfachen oder verlorenen Signalisierungen sind mir damit
>>noch nicht untergekommen.
>
> Du hast Recht. Verloren gegangene Signale halte ich für ein größte
> Gefahrenquelle bei Bedingungsvariablen. Dazu kommen noch die
> seltsamen "spurious wakeups".
>
>> Da ich allerdings noch in C++03 unterwegs bin,kann ich jetzt nicht mit
>> Beispielcode dienen.
>
> Das ist meine Grundannahme. Ich will C++11 verwenden. Da gibt es keine
> Semaphoren.

Huch, wer denkt sich denn sowas aus?! Gibt es Betriebssysteme ohne
Semaphoren? Ich kenne keine.

Man kann aber wohl mit Bedingungsvariablen Semaphoren nachbauen.
<http://stackoverflow.com/questions/4792449/c0x-has-no-semaphores-how-to-synchronize-threads>

Ich finde ja Semaphoren immer gut vorstellbar und gut verwendbar. Die
muss man dann also in C++11 immer aus Bedingungsvariablen bauen, und mit
Pech baut die Laufzeitbibliothek die Bedingungsvariablen aus "two
semaphores, a mutex and three counters"[1] zusammen. Klingt irgendwie
nicht wie eine besonders gute Idee...


Stefan

--
[1] http://www.open-std.org/jtc1/sc22/wg21/docs/papers/2007/n2406.html

M J

unread,
May 12, 2012, 3:12:31 PM5/12/12
to
On 12 mayo, 13:24, Stefan Reuther <stefan.n...@arcor.de> wrote:
> Hallo,
>
> Rainer Grimm wrote:
> >>[Semaphore]
> >>Probleme mit mehrfachen oder verlorenen Signalisierungen sind mir damit
> >>noch nicht untergekommen.
>
> > Du hast Recht. Verloren gegangene Signale halte ich für ein größte
> > Gefahrenquelle bei Bedingungsvariablen. Dazu kommen noch die
> > seltsamen "spurious wakeups".
>
> >> Da ich allerdings noch in C++03 unterwegs bin,kann ich jetzt nicht mit
> >> Beispielcode dienen.
>
> > Das ist meine Grundannahme. Ich will C++11 verwenden. Da gibt es keine
> > Semaphoren.
>
> Huch, wer denkt sich denn sowas aus?!

Bedingungsvariablen wurden u.a. eingeführt, um das Monitor-Schema zu
ermöglichen.[1] Verglichen mit Semaphoren gelten Monitore als i.Allg.
einfacher korrekt anwendbar.[2]


--
[1] http://www.open-std.org/jtc1/sc22/wg14/www/docs/n1196.htm
[2] http://de.wikipedia.org/wiki/Monitor_(Informatik)

Stefan Reuther

unread,
May 14, 2012, 1:20:58 PM5/14/12
to
M J wrote:
> On 12 mayo, 13:24, Stefan Reuther <stefan.n...@arcor.de> wrote:
>>Rainer Grimm wrote:
>>>Das ist meine Grundannahme. Ich will C++11 verwenden. Da gibt es keine
>>>Semaphoren.
>>
>>Huch, wer denkt sich denn sowas aus?!
>
> Bedingungsvariablen wurden u.a. eingeführt, um das Monitor-Schema zu
> ermöglichen.[1] Verglichen mit Semaphoren gelten Monitore als i.Allg.
> einfacher korrekt anwendbar.[2]

Ein Monitor ist doch auch nur ein glorifizierter Mutex / Kritischer
Abschnitt.

Ich finde nun Zählsemaphore deutlich einfacher andwendbar als
Bedingungsvariablen. Jedes Betriebssystem hat sie, und bisher jede
Multithread-Abstraktion für C++, die ich gebaut hab, hatte die drei
Klassen Thread, Mutex, Semaphore. Sie sind spottbillig und haben auch
keine Probleme mit spurious wakeups (was auf einem 64-Kern-System ganz
schön in die Performance haut).

Daher wundert mich diese Auslassung schon stark. Da werden wohl auch
meine künftigen C++-Multithread-Abstraktionen irgendwo <windows.h> und
<pthread.h> einbinden müssen :-(


Stefan

M J

unread,
May 15, 2012, 12:22:34 AM5/15/12
to
Mit Semaphoren basteln sich Programmierer angeblich öfter mal race
conditions (Wettläufe). Allerdings hat Rainer Grimm das mit
Bedingungsvariablen auch hingekriegt, wie man in comp.lang.c+
+.moderated nachlesen kann.

Betriebssystemunterstützung für Bedingungsvariablen:
- pthreads
- Windows ab Vista
Gibt es noch mehr?

Stefan Reuther

unread,
May 16, 2012, 10:01:28 AM5/16/12
to
M J wrote:
> On 14 mayo, 15:20, Stefan Reuther <stefan.n...@arcor.de> wrote:
[C++11 hat keine Semaphoren]
>>Ich finde nun Zählsemaphore deutlich einfacher andwendbar als
>>Bedingungsvariablen. Jedes Betriebssystem hat sie, und bisher jede
>>Multithread-Abstraktion für C++, die ich gebaut hab, hatte die drei
>>Klassen Thread, Mutex, Semaphore. Sie sind spottbillig und haben auch
>>keine Probleme mit spurious wakeups (was auf einem 64-Kern-System ganz
>>schön in die Performance haut).
>>
>>Daher wundert mich diese Auslassung schon stark. Da werden wohl auch
>>meine künftigen C++-Multithread-Abstraktionen irgendwo <windows.h> und
>><pthread.h> einbinden müssen :-(
>
> Mit Semaphoren basteln sich Programmierer angeblich öfter mal race
> conditions (Wettläufe). Allerdings hat Rainer Grimm das mit
> Bedingungsvariablen auch hingekriegt, wie man in comp.lang.c+
> +.moderated nachlesen kann.

Diese Feststellung, dass Semaphoren schwerer korrekt zu verwenden seien
als Bedingungsvariablen, begegnet mir halt hier zum ersten Mal, und mein
Google-Fu reicht nicht aus, um dazu schöne Papers zu finden.

> Betriebssystemunterstützung für Bedingungsvariablen:
> - pthreads
> - Windows ab Vista
> Gibt es noch mehr?

"Windows ab Vista" ist schon mal kein Argument *für* Bedingungs-
variablen, solange es ältere Windowse noch in Stückzahlen gibt. Außerdem
ist es da eine Userland-Implementierung, funktioniert also nicht über
Prozessgrenzen hinweg wie ein Mutex oder Semaphore.

Die Embedded-Randgruppen-Betriebssysteme, mit denen ich in letzter Zeit
gearbeitet habe (INTEGRITY, SYSBIOS) haben ebenfalls Mutex und Semaphore
als Kernelobjekt und Bedingungsvariablen, wenn überhaupt, im Userland.
Zugegeben, die Compiler dafür sind mit Ach und Krach C++98-fähig,
niemand wird dort eine C++11-Bibliothek fordern; andererseits macht es
den Umstieg auf C++11 natürlich nicht leichter, wenn man dazu erstmal
das Betriebssystem umbauen muss.


Stefan

Vinzent Hoefler

unread,
May 16, 2012, 2:40:02 PM5/16/12
to
Stefan Reuther wrote:

> Diese Feststellung, dass Semaphoren schwerer korrekt zu verwenden seien
> als Bedingungsvariablen, begegnet mir halt hier zum ersten Mal, und mein
> Google-Fu reicht nicht aus, um dazu schöne Papers zu finden.

<http://comp.ist.utl.pt/ee-se/acetatos/chap8-2.PDF>

S. 9ff.


Vinzent.

--
The most likely way for the world to be destroyed, most experts agree,
is by accident. That's where we come in; we're computer professionals.
We cause accidents.
-- Nathaniel Borenstein
Message has been deleted

Stefan Reuther

unread,
May 21, 2012, 6:06:57 AM5/21/12
to
Michael Andres wrote:
> On 16 mayo, 10:01, Stefan Reuther <stefan.n...@arcor.de> wrote:
>>>Betriebssystemunterstützung für Bedingungsvariablen:
>>>- pthreads
>>>- Windows ab Vista
>>>Gibt es noch mehr?
>>
>>"Windows ab Vista" ist schon mal kein Argument *für* Bedingungs-
>>variablen, solange es ältere Windowse noch in Stückzahlen gibt. Außerdem
>>ist es da eine Userland-Implementierung, funktioniert also nicht über
>>Prozessgrenzen hinweg wie ein Mutex oder Semaphore.
>>
>>Die Embedded-Randgruppen-Betriebssysteme, mit denen ich in letzter Zeit
>>gearbeitet habe (INTEGRITY, SYSBIOS) haben ebenfalls Mutex und Semaphore
>>als Kernelobjekt und Bedingungsvariablen, wenn überhaupt, im Userland.
>>Zugegeben, die Compiler dafür sind mit Ach und Krach C++98-fähig,
>>niemand wird dort eine C++11-Bibliothek fordern; andererseits macht es
>>den Umstieg auf C++11 natürlich nicht leichter, wenn man dazu erstmal
>>das Betriebssystem umbauen muss.
>
> Man muss es ja nicht umbauen. Die C++11-Emulation läuft ja auch so.
> Nur eben langsamer als sie könnte.

Das ist immer noch kein gutes Argument, speziell da die genannten
Betriebssysteme unter anderem damit werben, wie gering die Reaktions-
zeit auf eine gelupfte Semaphore ist.

> Ich finde, Präsenz auf Betriebssystemen darf kein Killer-Argument für
> oder gegen neue Features sein, denn die Betriebssystemhersteller
> können nicht alle Entwicklungen in Programmiersprachen antizipieren.

Es war lange genug ein Argument dafür, dass C++ immer noch kein
opendir/readdir-Äquivalent oder gar Netzwerk enthält, obwohl sich da
sogar die Embedded-Hersteller Mühe geben, sowas auf Systemen, die es
eigentlich nicht können, im Debugger zu simulieren.

> Die Verbreitung von C++ ist groß genug, um auch einen Einfluss auf
> Betriebssystementwicklungen nehmen zu können, insbesondere, wenn der
> betreffende Betriebssystemhersteller auch Compiler baut.

Eine Semaphore-aus-Condition-Variablen braucht aber in jedem Fall für
das Lupfen der Semaphore ("sem_post") eine Warteoperation. Das ist,
finde ich, für Echtzeitanwendungen unbrauchbar. Damit holt man sich
allerlei Prioritätsinversionen ins Haus und Dinge wie "aus dem Interrupt
eine Semaphore freigeben" (oder, etwas ziviler, "aus einem Signal-
Handler...") kann man auch vergessen.

Insofern kann ich nur für mich festhalten, dass ich auch weiterhin
eigene Portabilitäts-Layer brauchen werde, und das finde ich doch recht
schade.


Stefan

Stefan Reuther

unread,
May 21, 2012, 6:08:35 AM5/21/12
to
Vinzent Hoefler wrote:
> Stefan Reuther wrote:
>> Diese Feststellung, dass Semaphoren schwerer korrekt zu verwenden seien
>> als Bedingungsvariablen, begegnet mir halt hier zum ersten Mal, und mein
>> Google-Fu reicht nicht aus, um dazu schöne Papers zu finden.
>
> <http://comp.ist.utl.pt/ee-se/acetatos/chap8-2.PDF>
>
> S. 9ff.

Das ist aber mehr ein Argument für sprachunterstützte Monitore, und die
gibt's ja in C++ weiterhin nicht.


Stefan
0 new messages