Hypercell ein ] Hypercell aus ] Zeige Navigation ] Verstecke Navigation ]
c++.de  
   
Forentreff 2012     
Bücher-Shop mit Amazon (Buchkategorien)C++ : Referenzen zu C++ : C++ Builder : Visual C++ : C# : Java : Spieleprogrammierung : Systemprogrammierung Linux : Software-Entwicklung : .NET : Compilertechnik : Algorithmen & Datenstrukturen : Objektorientierung : Entwurfsmuster : UML : eXtreme Programming : Scrum : Projektmanagement : Software-Testing : Datenbanken : Tom DeMarco : Dilbert : User Friendly
C/C++ Forum :: FAQ - WinAPI ::  Critical Section vs. Mutex Object vs. Interlocked Access     Zeige alle Beiträge auf einer Seite Auf Beitrag antworten
Autor Nachricht
FrEEzE2046
Mitglied

Benutzerprofil
Anmeldungsdatum: 03.12.2008
Beiträge: 786
Beitrag FrEEzE2046 Mitglied 00:17:45 31.07.2010   Titel:   Critical Section vs. Mutex Object vs. Interlocked Access            Zitieren

Hallo,

ich greife von mehreren Threads auf eine gemeinsame Ressource zu. Diese sieht ungefähr wie folgt aus:

C/C++ Code:
struct
{
    int  id;
    bool locked;
    // [...]
};
C/C++ Code:
struct
{
int id;
bool locked;
// [...]
};
C/C++ Code:
struct
{
    int  id;
    bool locked;
    // [...]
};


Die Threads suchen eine bestimme id. Werden sie fündig, so sollen sie locked auf true setzen. Hat ein Thread ein solches Objekt gelockt, so kann er die id ggf. verändern (könnte problematisch sein).

Das Problem ist folgendes: Hat ein Thread seine gesuchte id gefunden, kann aber nicht auf das Objekt zugreifen, da locked == true, so soll er warten bis locked == false ist. Wie gesagt kann aber rein theoretisch die id zunächst die korrekte sein, locked == true und "plötzlich" die id sich verändern ...

Ich brauche jedenfalls einen Weg um die gemeinsamen Ressourcen (wie die Variable "locked") zu schützen.

Ich habe folgendes Test-Szenario erstellt:

C/C++ 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
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
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
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
#include <iostream>
#include
<process.h>
#include
<Windows.h>

using namespace std;

int const THREAD_COUNT = 4;
int const LOOP_COUNT   = 10;

long volatile lock;
long count = 0;

unsigned __stdcall ThreadFunc(void* ptr);


int main()
{
    HANDLE hThread[THREAD_COUNT] = {nullptr};
    long var[THREAD_COUNT];

    for( int i = 0; i < _countof(hThread); ++i )
    {
        var[i] = i;
        hThread[i] = reinterpret_cast<HANDLE>(_beginthreadex(0,0,ThreadFunc,&var[i],CREATE_SUSPENDED,NULL));
    }

    SetProcessAffinityMask(GetCurrentProcess(), 15);
    for( int i = 0; i < _countof(hThread); ++i )
    {
        SetThreadAffinityMask(hThread[i], 1 << i);
        SetThreadPriority(hThread[i], THREAD_PRIORITY_HIGHEST);
    }

    for( int i = 0; i < _countof(hThread); ++i ) {
        ResumeThread(hThread[i]);
    }

    WaitForMultipleObjects(_countof(hThread), hThread, TRUE, INFINITE);

    for( int i = 0; i < _countof(hThread); ++i ) {
        CloseHandle(hThread[i]);
    }

    cout << "finished" << endl;
    return 0;
}


unsigned __stdcall ThreadFunc(void* ptr)
{
    long* id = static_cast<long*>(ptr);
    int iter = 0;

    while( InterlockedExchange(&lock, 1) != 0 ) {Sleep(1);}

    for( int i = 0; i < LOOP_COUNT; ++i ) {
        printf("thread(%d/%d) output: %.8d\n", *id, iter, count++);
    }

    InterlockedExchange(&lock, 0);
    return 0;
}
C/C++ 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
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
#include <iostream>
#include
<process.h>
#include
<Windows.h>

using namespace std;

int const THREAD_COUNT = 4;
int const LOOP_COUNT = 10;

long volatile lock;
long count = 0;

unsigned __stdcall ThreadFunc(void* ptr);


int main()
{
HANDLE hThread[THREAD_COUNT] = {nullptr};
long var[THREAD_COUNT];

for( int i = 0; i < _countof(hThread); ++i )
{
var[i] = i;
hThread[i] = reinterpret_cast<HANDLE>(_beginthreadex(0,0,ThreadFunc,&var[i],CREATE_SUSPENDED,NULL));
}

SetProcessAffinityMask(GetCurrentProcess(), 15);
for( int i = 0; i < _countof(hThread); ++i )
{
SetThreadAffinityMask(hThread[i], 1 << i);
SetThreadPriority(hThread[i], THREAD_PRIORITY_HIGHEST);
}

for( int i = 0; i < _countof(hThread); ++i ) {
ResumeThread(hThread[i]);
}

WaitForMultipleObjects(_countof(hThread), hThread, TRUE, INFINITE);

for( int i = 0; i < _countof(hThread); ++i ) {
CloseHandle(hThread[i]);
}

cout << "finished" << endl;
return 0;
}


unsigned __stdcall ThreadFunc(void* ptr)
{
long* id = static_cast<long*>(ptr);
int iter = 0;

while( InterlockedExchange(&lock, 1) != 0 ) {Sleep(1);}

for( int i = 0; i < LOOP_COUNT; ++i ) {
printf("thread(%d/%d) output: %.8d\n", *id, iter, count++);
}

InterlockedExchange(&lock, 0);
return 0;
}
C/C++ 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
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
#include <iostream>
#include
<process.h>
#include
<Windows.h>

using namespace std;

int const THREAD_COUNT = 4;
int const LOOP_COUNT   = 10;

long volatile lock;
long count = 0;

unsigned __stdcall ThreadFunc(void* ptr);


int main()
{
    HANDLE hThread[THREAD_COUNT] = {nullptr};
    long var[THREAD_COUNT];

    for( int i = 0; i < _countof(hThread); ++i )
    {
        var[i] = i;
        hThread[i] = reinterpret_cast<HANDLE>(_beginthreadex(0,0,ThreadFunc,&var[i],CREATE_SUSPENDED,NULL));
    }

    SetProcessAffinityMask(GetCurrentProcess(), 15);
    for( int i = 0; i < _countof(hThread); ++i )
    {
        SetThreadAffinityMask(hThread[i], 1 << i);
        SetThreadPriority(hThread[i], THREAD_PRIORITY_HIGHEST);
    }

    for( int i = 0; i < _countof(hThread); ++i ) {
        ResumeThread(hThread[i]);
    }

    WaitForMultipleObjects(_countof(hThread), hThread, TRUE, INFINITE);

    for( int i = 0; i < _countof(hThread); ++i ) {
        CloseHandle(hThread[i]);
    }

    cout << "finished" << endl;
    return 0;
}


unsigned __stdcall ThreadFunc(void* ptr)
{
    long* id = static_cast<long*>(ptr);
    int iter = 0;

    while( InterlockedExchange(&lock, 1) != 0 ) {Sleep(1);}

    for( int i = 0; i < LOOP_COUNT; ++i ) {
        printf("thread(%d/%d) output: %.8d\n", *id, iter, count++);
    }

    InterlockedExchange(&lock, 0);
    return 0;
}


Hier nutze ich die globale Variable lock um den Bereich in dem count verändert und die Konsoleausgabe stattfindet zu schützen.
Mit dem Sleep(1) innerhalb der while-Schleife habe ich auch eine relativ gute Performance (Warum ist dies bei Sleep(0) nicht so? Bzw. welche Rolle übernimmt Sleep() hier eigentlich?).

Eine Critical Section scheint mir jedoch performanter zu sein:

C/C++ Code:
1
2
3
4
5
6
7
8
9
10
11
12
13
1
2
3
4
5
6
7
8
9
10
11
12
13
unsigned __stdcall ThreadFunc(void* ptr)
{
    long* id = static_cast<long*>(ptr);
    int iter = 0;

    EnterCriticalSection(&cs);
    for( int i = 0; i < LOOP_COUNT; ++i ) {
        printf("thread(%d/%d) output: %.8d\n", *id, iter, count++);
    }
    LeaveCriticalSection(&cs);
   
    return 0;
}
C/C++ Code:
1
2
3
4
5
6
7
8
9
10
11
12
13
unsigned __stdcall ThreadFunc(void* ptr)
{
long* id = static_cast<long*>(ptr);
int iter = 0;

EnterCriticalSection(&cs);
for( int i = 0; i < LOOP_COUNT; ++i ) {
printf("thread(%d/%d) output: %.8d\n", *id, iter, count++);
}
LeaveCriticalSection(&cs);

return 0;
}
C/C++ Code:
1
2
3
4
5
6
7
8
9
10
11
12
13
unsigned __stdcall ThreadFunc(void* ptr)
{
    long* id = static_cast<long*>(ptr);
    int iter = 0;

    EnterCriticalSection(&cs);
    for( int i = 0; i < LOOP_COUNT; ++i ) {
        printf("thread(%d/%d) output: %.8d\n", *id, iter, count++);
    }
    LeaveCriticalSection(&cs);
   
    return 0;
}


Auch ein Mutex Object ist denkbar:
C/C++ Code:
1
2
3
4
5
6
7
8
9
10
11
12
13
1
2
3
4
5
6
7
8
9
10
11
12
13
unsigned __stdcall ThreadFunc(void* ptr)
{
    long* id = static_cast<long*>(ptr);
    int iter = 0;

    WaitForSingleObject(hMutex, INFINITE);
    for( int i = 0; i < LOOP_COUNT; ++i ) {
        printf("thread(%d/%d) output: %.8d\n", *id, iter, count++);
    }
    ReleaseMutex(hMutex);

    return 0;
}
C/C++ Code:
1
2
3
4
5
6
7
8
9
10
11
12
13
unsigned __stdcall ThreadFunc(void* ptr)
{
long* id = static_cast<long*>(ptr);
int iter = 0;

WaitForSingleObject(hMutex, INFINITE);
for( int i = 0; i < LOOP_COUNT; ++i ) {
printf("thread(%d/%d) output: %.8d\n", *id, iter, count++);
}
ReleaseMutex(hMutex);

return 0;
}
C/C++ Code:
1
2
3
4
5
6
7
8
9
10
11
12
13
unsigned __stdcall ThreadFunc(void* ptr)
{
    long* id = static_cast<long*>(ptr);
    int iter = 0;

    WaitForSingleObject(hMutex, INFINITE);
    for( int i = 0; i < LOOP_COUNT; ++i ) {
        printf("thread(%d/%d) output: %.8d\n", *id, iter, count++);
    }
    ReleaseMutex(hMutex);

    return 0;
}



Bekanntlich führen viele Wege nach Rom. Dennoch hat man die unterschiedlichen Funktionen sicherlich nicht grundlos implementiert und es gibt sicherlich die ein oder anderen Vor- u. Nachteile.

Mein Problem ist, dass mir trotz intensiver Studie der MSDN es schwer fällt, die einzelnen Herangehensweisen zu unterscheiden.

Die Interlocked Zugriffe auf Variablen sind sicherlich vorrangig zum Vollzug atomarer Operationen durch die fence-Instruktionen.

Kann mir jemand weiterhelfen?


Zuletzt bearbeitet von FrEEzE2046 am 00:17:59 31.07.2010, insgesamt 1-mal bearbeitet
Werbeunterbrechung
RedPuma
Mitglied

Benutzerprofil
Anmeldungsdatum: 24.01.2008
Beiträge: 165
Beitrag RedPuma Mitglied 12:42:41 31.07.2010   Titel:              Zitieren

Also, Kurzzusammenfassung:

Die Interlocked Funktionen sind CPU spezifische Operationen welche das sichere Inkrementieren/Dekrementieren/Setzen/Lesen/etc. einer Variable ermöglichen. Also wenn du eine shared Variable x hast, dann kannst du über die Interlocked Funktionen (und nur über diese) sicher auf diese Variable zugreifen.
Der Vorteil liegt hierbei eindeutig in der Geschwindigkeit, die Interlocked Funktionen sind (wegen der ziemlichen Hardwarenähe) eindeutig die schnellste Synchronisationsart, jedoch nicht für alle Fälle sinnvoll.

Die Critical Section ist praktisch fast das selbe wie ein Mutex nur etwas schneller und auf einen Prozess begrenzt. Critical Sections sind empfehlenswert wenn die kritische Codestelle nur selten von anderen Threads genutzt wird, also in der Regel nur selten auf die "Freigabe" eines anderen Threads gewartet werden muss. Critical Sections sind sehr schnell wenn es darum geht nur zu überprüfen ob eine Codestelle gelocked ist oder nicht, und daher im oben genannten Fällen empfehlenswert. Falls die Codestelle momentan von einem anderen Thread "benutzt" wird, wird zuerst eine gewisse Anzahl CPU-Zyklen auf "Volllast", d.h. aktiv gewartet (die Anzahl der Zyklen lässt sich über den Spin-Count festlegen). Anschließend wird ein normaler Mutex alloziert und benutzt, was das ganze dann schon bedeutend langsamer werden lässt. Critical Section sollten für die meisten Inter-Thread Synchronisations-Szenarien ausreichend sein. Bei besonders zeitkritischen Anwendungen kann man den Spin-Count ensprechend hoch einstellen, natürlich auf Kosten der CPU-Auslastung, da ja aktiv (aus Programmierersicht könnte man sagen in einer Endlosschleife) gewartet wird.

Das dritte Objekt im Bunde ist der Mutex, der langsame Rundumschlag Microsofts. Mutex unterstützen sowohl Inter-Thread Synchronisation als auch Inter-Process Synchronisation, erfüllen aber im Grunde das selbe wie die Critical Section. Ein entscheidender Nachteil ist die Geschwindigkeit, da zum Einen bei jeder Abfrage des Mutex-Status in den Kernel-Mode geschaltet wird, und zum Anderen nicht "aktiv" auf ein Mutex-Objekt gewartet wird. Das Betriebssystem (bzw. dessen Thread-Scheduler) entscheidet also wann wieder zu einem "wartenden" Mutex zurückgesprungen wird.


Hm, doch keine so kurze Zusammenfassung, daher hier nochmal ne richtig kurze:
Interlocked: Sehr sehr schnell, aber sehr speziell
Critical Section: Objekt der Wahl für Inter-Thread Synchronisation innerhalb eines Prozesses.
Mutex: Rundumschlag, gut für Inter-Prozess Synchronisation.
hustbaer
Mitglied

Benutzerprofil
Anmeldungsdatum: 27.10.2006
Beiträge: 13522
Beitrag hustbaer Mitglied 15:57:50 31.07.2010   Titel:              Zitieren

Die Interlocked-Funktionen sind flexibler. Damit kannst du z.B. auch lockfreie Datenstrukturen implementieren. Also Datenstrukturen die ganz ohne extra "Mutex" oder CRITICAL_SECTION auskommen.
Ich würde empfehlen da grundsätzlich die Finger davon zu lassen, ausser du hast a) sehr spezielle Anforderungen und b) kennst dich schon sehr gut mit dem Thema aus.

CRITICAL_SECTION ist die "schnelle" Mutex-Variante von Windows. Kann man eigentlich überall verwenden, es sei denn man braucht ein Feature welches CRITICAL_SECTION nicht bietet. Das könnte sein
* Prozessübergreifend verwendbar (CRITICAL_SECTION ist nur innerhalb eines einzigen Prozesses verwendbar)
* Timed-Wait Funktion (CRITICAL_SECTION bietet nur TryEnter)
* Deterministisches Scheduling (z.B. FIFO, Priority)

Eine Windows Kernel Mutex schliesslich bietet die ersten beiden Features. Dafür sind die Dinger verdammt viel langsamer als CRITICAL_SECTIONs.


Meine Empfehlung wäre die erste mögliche Option aus dieser Liste zu verwenden:
1) Libraries ala Boost.Thread verwenden die das ganze abstrahieren/kapseln
2) CRITICAL_SECTION
3) Windows Kernel Mutex
4) Nur wenn's gar nicht anders geht Interlocked-Funktionen

_________________
"Let there be Licht..." http://lichttools.sourceforge.net/
Sehr cooles ASCII Spiel (leider nicht von mir): ASCII-Scramble - http://www.roskakori.at/ascii/
RedPuma
Mitglied

Benutzerprofil
Anmeldungsdatum: 24.01.2008
Beiträge: 165
Beitrag RedPuma Mitglied 16:27:34 31.07.2010   Titel:              Zitieren

hustbaer schrieb:

1) Libraries ala Boost.Thread verwenden die das ganze abstrahieren/kapseln


Ich wüsste nicht was die Boost Library anders machen sollte außer Critical Sections zu verwenden. Und wenn du unbedingt ne Objektorientierte Kapselung verwenden willst dann bitte:

Header:
C/C++ Code:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
#pragma once

#include
<Windows.h>

//CThreadMutex is a wrapper for the Windows Mutex Synchronization Object

class CThreadMutex
{
public:
    CThreadMutex(void);
    ~CThreadMutex(void);

    void SetOrWait( void );
    void Release( void );

private:
    CRITICAL_SECTION m_Crit;
};
C/C++ Code:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
#pragma once

#include
<Windows.h>

//CThreadMutex is a wrapper for the Windows Mutex Synchronization Object

class CThreadMutex
{
public:
CThreadMutex(void);
~CThreadMutex(void);

void SetOrWait( void );
void Release( void );

private:
CRITICAL_SECTION m_Crit;
};
C/C++ Code:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
#pragma once

#include
<Windows.h>

//CThreadMutex is a wrapper for the Windows Mutex Synchronization Object

class CThreadMutex
{
public:
    CThreadMutex(void);
    ~CThreadMutex(void);

    void SetOrWait( void );
    void Release( void );

private:
    CRITICAL_SECTION m_Crit;
};


.cpp File:
C/C++ 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
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
#include "stdafx.h"
#include
"CThreadMutex.h"
#include
<Windows.h>

CThreadMutex::CThreadMutex(void)
{
    InitializeCriticalSection( &m_Crit );
}


CThreadMutex::~CThreadMutex(void)
{
    DeleteCriticalSection( &m_Crit );
}


void CThreadMutex::SetOrWait( void )
{
    EnterCriticalSection( &m_Crit );
}


void CThreadMutex::Release( void )
{
    LeaveCriticalSection( &m_Crit );
}
C/C++ 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
#include "stdafx.h"
#include
"CThreadMutex.h"
#include
<Windows.h>

CThreadMutex::CThreadMutex(void)
{
InitializeCriticalSection( &m_Crit );
}


CThreadMutex::~CThreadMutex(void)
{
DeleteCriticalSection( &m_Crit );
}


void CThreadMutex::SetOrWait( void )
{
EnterCriticalSection( &m_Crit );
}


void CThreadMutex::Release( void )
{
LeaveCriticalSection( &m_Crit );
}
C/C++ 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
#include "stdafx.h"
#include
"CThreadMutex.h"
#include
<Windows.h>

CThreadMutex::CThreadMutex(void)
{
    InitializeCriticalSection( &m_Crit );
}


CThreadMutex::~CThreadMutex(void)
{
    DeleteCriticalSection( &m_Crit );
}


void CThreadMutex::SetOrWait( void )
{
    EnterCriticalSection( &m_Crit );
}


void CThreadMutex::Release( void )
{
    LeaveCriticalSection( &m_Crit );
}


Heißt leider noch CThreadMutex weil ich die Klasse zuerst für Mutexes genutzt habe und anschließend auf Critical Sections umgeschrieben habe, ich wollte aber keine weitreichenden Änderungen im Code meines Programms machen.

Kleines Beispiel:
C/C++ Code:
1
2
3
4
5
6
7
8
9
1
2
3
4
5
6
7
8
9
CThreadMutex myMutex;

//...

myMutex.SetOrWait();

//do something critical

myMutex.Release();
C/C++ Code:
1
2
3
4
5
6
7
8
9
CThreadMutex myMutex;

//...

myMutex.SetOrWait();

//do something critical

myMutex.Release();
C/C++ Code:
1
2
3
4
5
6
7
8
9
CThreadMutex myMutex;

//...

myMutex.SetOrWait();

//do something critical

myMutex.Release();
Fräge!
Unregistrierter




Beitrag Fräge! Unregistrierter 18:34:16 31.07.2010   Titel:              Zitieren

Was ist mit den Interlocked-Funktionen? Sehr speziell? Nur, wenn's gar nicht anders geht?
Gibt es da Probleme? Ich nutze die überall, wo ich eine Variable synchronisieren muss.
RedPuma
Mitglied

Benutzerprofil
Anmeldungsdatum: 24.01.2008
Beiträge: 165
Beitrag RedPuma Mitglied 19:00:25 31.07.2010   Titel:              Zitieren

Fräge! schrieb:
Was ist mit den Interlocked-Funktionen? Sehr speziell? Nur, wenn's gar nicht anders geht?
Gibt es da Probleme? Ich nutze die überall, wo ich eine Variable synchronisieren muss.


Speziell in dem Sinne dass man damit IMHO nur einzelne Variablen synchronisieren kann, und keine ganzen Codeabschnitte. Die Meinung dass man sie nur benutzen soll wenns gar nicht anders geht teile ich nicht, schließlich sind die eigentlich relativ einfach zu benutzen.
volkard
Moderator

Benutzerprofil
Anmeldungsdatum: 06.04.2000
Beiträge: 24355
Beitrag volkard Moderator 19:10:47 31.07.2010   Titel:              Zitieren

RedPuma schrieb:
Die Meinung dass man sie nur benutzen soll wenns gar nicht anders geht teile ich nicht, schließlich sind die eigentlich relativ einfach zu benutzen.

Naja, ich versuche immer das schnellste zu benutzen. Interlocked ist da schon recht weit vorne. Aber nicht vergessen man darf, daß die CritSect schon schneller als nur zwei Interlockeds ist.

_________________
http://www.venganza.info/
plonk fürs Forum v1.02
[e]Pi[/e]
Unregistrierter




Beitrag [e]Pi[/e] Unregistrierter 19:14:24 31.07.2010   Titel:              Zitieren

Zitat:
Aber nicht vergessen man darf, daß die CritSect schon schneller als nur zwei Interlockeds ist.

Wirklich das stimmt? Kann jemand bestätigen?
volkard
Moderator

Benutzerprofil
Anmeldungsdatum: 06.04.2000
Beiträge: 24355
Beitrag volkard Moderator 19:38:31 31.07.2010   Titel:              Zitieren

Π schrieb:
Zitat:
Aber nicht vergessen man darf, daß die CritSect schon schneller als nur zwei Interlockeds ist.

Wirklich das stimmt? Kann jemand bestätigen?

Bei mir:
CritSect 3.047 Sekunden
Interlocked 3.406

Es muß mehrmals gemessen und der kleinste Wert genommen werden.
C/C++ 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
28
29
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
#include <iostream>
#include
<ctime>
#include
<Windows.h>
using namespace std;

int main(){
    CRITICAL_SECTION cs;
    LONG su=0;
    LONG lo=0;
    InitializeCriticalSection(&cs);
    clock_t cl=clock();
    while(cl==clock())
        ;
    cl=clock();
    for(LONG i=0;i<100000000;++i){
#if
0
        EnterCriticalSection(&cs);
        su+=i;
        LeaveCriticalSection(&cs);
#else

        InterlockedExchange(&lo,su);
        su+=i;
        InterlockedExchange(&lo,i);
#endif

    }
    DeleteCriticalSection(&cs);
    cout<<(clock()-cl)/double(CLOCKS_PER_SEC)<<" Sekunden\n";
    cout<<"Antioptimierungsausgabe: "<<lo<<su<<'\n';
}
C/C++ 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
28
29
#include <iostream>
#include
<ctime>
#include
<Windows.h>
using namespace std;

int main(){
CRITICAL_SECTION cs;
LONG su=0;
LONG lo=0;
InitializeCriticalSection(&cs);
clock_t cl=clock();
while(cl==clock())
;
cl=clock();
for(LONG i=0;i<100000000;++i){
#if
0
EnterCriticalSection(&cs);
su+=i;
LeaveCriticalSection(&cs);
#else

InterlockedExchange(&lo,su);
su+=i;
InterlockedExchange(&lo,i);
#endif

}
DeleteCriticalSection(&cs);
cout<<(clock()-cl)/double(CLOCKS_PER_SEC)<<" Sekunden\n";
cout<<"Antioptimierungsausgabe: "<<lo<<su<<'\n';
}
C/C++ 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
28
29
#include <iostream>
#include
<ctime>
#include
<Windows.h>
using namespace std;

int main(){
    CRITICAL_SECTION cs;
    LONG su=0;
    LONG lo=0;
    InitializeCriticalSection(&cs);
    clock_t cl=clock();
    while(cl==clock())
        ;
    cl=clock();
    for(LONG i=0;i<100000000;++i){
#if
0
        EnterCriticalSection(&cs);
        su+=i;
        LeaveCriticalSection(&cs);
#else

        InterlockedExchange(&lo,su);
        su+=i;
        InterlockedExchange(&lo,i);
#endif

    }
    DeleteCriticalSection(&cs);
    cout<<(clock()-cl)/double(CLOCKS_PER_SEC)<<" Sekunden\n";
    cout<<"Antioptimierungsausgabe: "<<lo<<su<<'\n';
}


Würde ich interessieren, ob es bei Euch ähnlich ist.

_________________
http://www.venganza.info/
plonk fürs Forum v1.02
hustbaer
Mitglied

Benutzerprofil
Anmeldungsdatum: 27.10.2006
Beiträge: 13522
Beitrag hustbaer Mitglied 20:38:06 31.07.2010   Titel:              Zitieren

RedPuma schrieb:
hustbaer schrieb:

1) Libraries ala Boost.Thread verwenden die das ganze abstrahieren/kapseln


Ich wüsste nicht was die Boost Library anders machen sollte außer Critical Sections zu verwenden.

Sie verwendet in der aktuellen Version soweit ich weiss Interlocked Funktionen, aber das ist eigentlich nicht wichtig.
Zitat:

Und wenn du unbedingt ne Objektorientierte Kapselung verwenden willst dann bitte:

Header:
C/C++ Code:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
#pragma once

#include
<Windows.h>

//CThreadMutex is a wrapper for the Windows Mutex Synchronization Object

class CThreadMutex
{
public:
    CThreadMutex(void);
    ~CThreadMutex(void);

    void SetOrWait( void );
    void Release( void );

private:
    CRITICAL_SECTION m_Crit;
};
C/C++ Code:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
#pragma once

#include
<Windows.h>

//CThreadMutex is a wrapper for the Windows Mutex Synchronization Object

class CThreadMutex
{
public:
CThreadMutex(void);
~CThreadMutex(void);

void SetOrWait( void );
void Release( void );

private:
CRITICAL_SECTION m_Crit;
};
C/C++ Code:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
#pragma once

#include
<Windows.h>

//CThreadMutex is a wrapper for the Windows Mutex Synchronization Object

class CThreadMutex
{
public:
    CThreadMutex(void);
    ~CThreadMutex(void);

    void SetOrWait( void );
    void Release( void );

private:
    CRITICAL_SECTION m_Crit;
};


.cpp File:
C/C++ Code:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
#include "stdafx.h"
#include
"CThreadMutex.h"
#include
<Windows.h>

CThreadMutex::CThreadMutex(void)
{
    InitializeCriticalSection( &m_Crit );
}

CThreadMutex::~CThreadMutex(void)
{
    DeleteCriticalSection( &m_Crit );
}

void CThreadMutex::SetOrWait( void )
{
    EnterCriticalSection( &m_Crit );
}

void CThreadMutex::Release( void )
{
    LeaveCriticalSection( &m_Crit );
}
C/C++ Code:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
#include "stdafx.h"
#include
"CThreadMutex.h"
#include
<Windows.h>

CThreadMutex::CThreadMutex(void)
{
InitializeCriticalSection( &m_Crit );
}

CThreadMutex::~CThreadMutex(void)
{
DeleteCriticalSection( &m_Crit );
}

void CThreadMutex::SetOrWait( void )
{
EnterCriticalSection( &m_Crit );
}

void CThreadMutex::Release( void )
{
LeaveCriticalSection( &m_Crit );
}
C/C++ Code:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
#include "stdafx.h"
#include
"CThreadMutex.h"
#include
<Windows.h>

CThreadMutex::CThreadMutex(void)
{
    InitializeCriticalSection( &m_Crit );
}

CThreadMutex::~CThreadMutex(void)
{
    DeleteCriticalSection( &m_Crit );
}

void CThreadMutex::SetOrWait( void )
{
    EnterCriticalSection( &m_Crit );
}

void CThreadMutex::Release( void )
{
    LeaveCriticalSection( &m_Crit );
}


Wieso sollte man sich immer alles selbst programmieren?
Etwas selbst zu machen, nur weil es geht, ist meist schlecht. Wenn man es macht um dabei zu lernen, OK. Wenn man es nur macht weil man einen furchtbaren Dickschädel hat, ist es aber fast immer ein Fehler.

* Die Boost.Thread ist quasi-Standard, und wird fast 1:1 in der Form wohl auch im neuen C++ Standard landen
* Die Boost.Thread ist "tried and true", und von Leuten programmiert die ziemlich gut wissen was sie tun
* Die Boost.Thread ist komfortabel zu handhaben, was ich von deiner Klasse nicht sagen würde. Es fehlt z.B. die "Scoped-Lock" Klasse, die die Mutex/CRITICAL_SECTION im Konstruktor lockt und im Destruktor unlockt

Und noch ganz konkret zum Thema warum ich es für eine schlechte Idee halte, sich selbst was zu basteln, was es schon (als allgemein akzeptierte Komponente) fertig gibt...
Die meisten Leute machen bei der Implementierung nicht nur Fehler, sondern gewöhnen sich dabei auch an, bestimmte Dinge nach ihrem eigenen Dickschädel zu machen, und nicht wie sie "alle anderen" machen. Reicht schon wenn etwas was überall "foo" heisst bei dir dann "bar" heisst. Der Effekt der Implementierungsfehler dürfte klar sein. Der Effekt von anderen Namen/Begriffen ist, dass man anfängt ein anderes Vokabular zu verwenden. Und das ist IMO immer ein Nachteil, da es eine unnötige Kommunikations-Barriere darstellt.

Konkret am Beispiel deines Codes:

Implementierungs-Fehler:

* Deine CThreadMutex Klasse ist copy-constructable, dürfte es aber nicht sein

* Deine CThreadMutex Klasse ist assignable, dürfte es aber nicht sein


Quality-Of-Implementation:

* Der Konstruktor verwendet InitializeCriticalSection. InitializeCriticalSection kann fehlschlagen, diesen Umstand aber nicht kommunizieren. -> Lieber InitializeCriticalSectionAndSpinCount verwenden

* Du bietest keine Scoped-Lock Klasse an

* Du bietest keine Try-Lock Funktion an


Namen:

* "CThreadMutex" ist ein seltsamer name (was soll das "Thread" im Namen?). Entweder CriticalSection, wenn man einen Windows-spezifischen Namen will, oder einfach Mutex/RecursiveMutex/RecursiveTryMutex.

* "SetOrWait" ist ein Name den ich *noch nie* irgendwo in dem Zusammenhang gelesen/gehört habe. (Und für mich persönlich auch kontraintuitiv - "set" assoziiere ich im Zusammenhang mit Threads mit Events). Üblich wären "Lock"/"Unlock", "Acquire"/"Release" und "Enter"/"Leave". (Manchmal auch "Wait", finde ich persönlich aber nicht gut. Allerdings immer noch besser als SetOrWait")

RedPuma schrieb:
Kleines Beispiel:
(...)


Kleines Beispiel mit Boost.Thread:
C/C++ Code:
1
2
3
4
5
6
7
8
9
1
2
3
4
5
6
7
8
9
boost::recursive_mutex g_mutex;

void foo()
{
    boost::unique_lock<boost::recursive_mutex> lock(g_mutex); // lock

    //do something


} // unlock
C/C++ Code:
1
2
3
4
5
6
7
8
9
boost::recursive_mutex g_mutex;

void foo()
{
boost::unique_lock<boost::recursive_mutex> lock(g_mutex); // lock

//do something


} // unlock
C/C++ Code:
1
2
3
4
5
6
7
8
9
boost::recursive_mutex g_mutex;

void foo()
{
    boost::unique_lock<boost::recursive_mutex> lock(g_mutex); // lock

    //do something


} // unlock

_________________
"Let there be Licht..." http://lichttools.sourceforge.net/
Sehr cooles ASCII Spiel (leider nicht von mir): ASCII-Scramble - http://www.roskakori.at/ascii/
FrEEzE2046
Mitglied

Benutzerprofil
Anmeldungsdatum: 03.12.2008
Beiträge: 786
Beitrag FrEEzE2046 Mitglied 21:05:13 31.07.2010   Titel:              Zitieren

Vielen Dank für eure Antworten.
Ich denke, dass es definitiv am schnellsten ist die Interlocked-Funktionen zu benutzen, so lange es sich nur um den Zugriff auf eine Variable handelt.

Dabei würde mich noch interessieren, ob diese zwingend als volatile deklariert sein muss, oder ob das hier keine Rolle spielt.


volkard schrieb:
Würde ich interessieren, ob es bei Euch ähnlich ist.

CS: 3.285s
Interlocked: 2.745s
hustbaer
Mitglied

Benutzerprofil
Anmeldungsdatum: 27.10.2006
Beiträge: 13522
Beitrag hustbaer Mitglied 21:11:41 31.07.2010   Titel:              Zitieren

volkard schrieb:

Bei mir:
CritSect 3.047 Sekunden
Interlocked 3.406
(...)
Würde ich interessieren, ob es bei Euch ähnlich ist.

Auf dem P4 (Northwood, 2.8 GHz) von Mutti (hab Urlaub :)).

CS: 16.797 sec
interlocked: 12.703 sec

Deck sich inetwa mit dem was ich in Erinnerung habe (damals auch auf einem P4 gemessen), nämlich dass eine CS (lock + unlock) ca. 2.5 * so lange braucht wie eine Interlocked-Funktion.

Ob nun 2 oder 3 Interlocked-Calls macht IMO keinen grossen Unterschied. Wichtig ist zu wissen, dass eine CS ausreichend schnell ist, so dass es sich nicht (OK: kaum jemals) auszahlt sich was eigenes zu stricken.

Boost ist auf mutterns Rechner nicht installiert, sonst hätte ich auch noch schnell mit boost::mutex und boost::recursive_mutex verglichen.

_________________
"Let there be Licht..." http://lichttools.sourceforge.net/
Sehr cooles ASCII Spiel (leider nicht von mir): ASCII-Scramble - http://www.roskakori.at/ascii/


Zuletzt bearbeitet von hustbaer am 21:12:46 31.07.2010, insgesamt 1-mal bearbeitet
hustbaer
Mitglied

Benutzerprofil
Anmeldungsdatum: 27.10.2006
Beiträge: 13522
Beitrag hustbaer Mitglied 21:20:00 31.07.2010   Titel:              Zitieren

FrEEzE2046 schrieb:
Dabei würde mich noch interessieren, ob diese zwingend als volatile deklariert sein muss, oder ob das hier keine Rolle spielt.

Wenn du NUR Interlocked Funktionen verwendest um auf die Variable zuzugreifen ist es egal.
Wenn du an bestimmten Stellen die Variable einfach nur lesen willst (oder auch schreiben), dann solltest du sie volatile machen.

Speziell sowas kann schnell in die Hose gehen:
C/C++ Code:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
LONG i_should_be_volatile = 0;

void some_thread_fn()
{
    // do stuff
    InterlockedExchange(&i_should_be_volatile, 1);
}

void foo()
{
    while (i_should_be_volatile == 0)
    {
        // do nothing
    }
}
C/C++ Code:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
LONG i_should_be_volatile = 0;

void some_thread_fn()
{
// do stuff
InterlockedExchange(&i_should_be_volatile, 1);
}

void foo()
{
while (i_should_be_volatile == 0)
{
// do nothing
}
}
C/C++ Code:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
LONG i_should_be_volatile = 0;

void some_thread_fn()
{
    // do stuff
    InterlockedExchange(&i_should_be_volatile, 1);
}

void foo()
{
    while (i_should_be_volatile == 0)
    {
        // do nothing
    }
}

Das wird ohne volatile in foo() hängen bleiben, wenn man mit Optimierungen compiliert.

Sobald man einen System-Call (oder auch nur einen Aufruf einer Funktion, die der Compiler nicht analysieren kann) in der Schleife einfügt, geht es dann.

D.h. so geht es mit den meisten Compilern, wobei es nichts ist, worauf man sich verlassen sollte:
C/C++ Code:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
int i_should_be_volatile = 0;

void some_thread_fn()
{
    // do stuff
    InterlockedExchange(&i_should_be_volatile, 1);
}

void foo()
{
    while (i_should_be_volatile == 0)
    {
        Sleep(0); // system-call, verhindert normalerweise das rausziehen des tests "i_should_be_volatile == 0" vor die schleife
    }
}
C/C++ Code:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
int i_should_be_volatile = 0;

void some_thread_fn()
{
// do stuff
InterlockedExchange(&i_should_be_volatile, 1);
}

void foo()
{
while (i_should_be_volatile == 0)
{
Sleep(0); // system-call, verhindert normalerweise das rausziehen des tests "i_should_be_volatile == 0" vor die schleife
}
}
C/C++ Code:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
int i_should_be_volatile = 0;

void some_thread_fn()
{
    // do stuff
    InterlockedExchange(&i_should_be_volatile, 1);
}

void foo()
{
    while (i_should_be_volatile == 0)
    {
        Sleep(0); // system-call, verhindert normalerweise das rausziehen des tests "i_should_be_volatile == 0" vor die schleife
    }
}

Wenn man InterlockedCompareExchange(&i_should_be_volatile, 0, 0) statt i_should_be_volatile == 0 verwendet, dann geht es auch (verlässlich) ohne volatile.

_________________
"Let there be Licht..." http://lichttools.sourceforge.net/
Sehr cooles ASCII Spiel (leider nicht von mir): ASCII-Scramble - http://www.roskakori.at/ascii/
FrEEzE2046
Mitglied

Benutzerprofil
Anmeldungsdatum: 03.12.2008
Beiträge: 786
Beitrag FrEEzE2046 Mitglied 21:21:11 31.07.2010   Titel:              Zitieren

Wäre es nicht sinnvoller Acquire u. Release Semantic in diesem Fall zu benutzen:

C/C++ Code:
while( InterlockedCompareExchangeAcquire(&lo, 1, 0) != 0 );
su+=i;
while( InterlockedCompareExchangeRelease(&lo, 0, 1) != 1 );
C/C++ Code:
while( InterlockedCompareExchangeAcquire(&lo, 1, 0) != 0 );
su+=i;
while( InterlockedCompareExchangeRelease(&lo, 0, 1) != 1 );
C/C++ Code:
while( InterlockedCompareExchangeAcquire(&lo, 1, 0) != 0 );
su+=i;
while( InterlockedCompareExchangeRelease(&lo, 0, 1) != 1 );
volkard
Moderator

Benutzerprofil
Anmeldungsdatum: 06.04.2000
Beiträge: 24355
Beitrag volkard Moderator 21:32:14 31.07.2010   Titel:              Zitieren

FrEEzE2046 schrieb:
Wäre es nicht sinnvoller Acquire u. Release Semantic in diesem Fall zu benutzen:

C/C++ Code:
while( InterlockedCompareExchangeAcquire(&lo, 1, 0) != 0 );
su+=i;
while( InterlockedCompareExchangeRelease(&lo, 0, 1) != 1 );
C/C++ Code:
while( InterlockedCompareExchangeAcquire(&lo, 1, 0) != 0 );
su+=i;
while( InterlockedCompareExchangeRelease(&lo, 0, 1) != 1 );
C/C++ Code:
while( InterlockedCompareExchangeAcquire(&lo, 1, 0) != 0 );
su+=i;
while( InterlockedCompareExchangeRelease(&lo, 0, 1) != 1 );

Weiß nicht.
Aber wenn ich es mache, habe ich anscheinend keinen Unterschied mehr zwischen Interlocked und CritSect. AMD Sempron 64 3000+ (oh, er kostet nur noch 15€).

_________________
http://www.venganza.info/
plonk fürs Forum v1.02
FrEEzE2046
Mitglied

Benutzerprofil
Anmeldungsdatum: 03.12.2008
Beiträge: 786
Beitrag FrEEzE2046 Mitglied 22:56:21 31.07.2010   Titel:              Zitieren

volkard schrieb:
Aber wenn ich es mache, habe ich anscheinend keinen Unterschied mehr zwischen Interlocked und CritSect. AMD Sempron 64 3000+ (oh, er kostet nur noch 15€).


Gut, es ging jetzt weniger um die Zeit. Viel mehr um die Sicherheit, dass "su" den Wert hat, den es haben soll.
hustbaer
Mitglied

Benutzerprofil
Anmeldungsdatum: 27.10.2006
Beiträge: 13522
Beitrag hustbaer Mitglied 06:53:03 01.08.2010   Titel:              Zitieren

FrEEzE2046 schrieb:
volkard schrieb:
Aber wenn ich es mache, habe ich anscheinend keinen Unterschied mehr zwischen Interlocked und CritSect. AMD Sempron 64 3000+ (oh, er kostet nur noch 15€).


Gut, es ging jetzt weniger um die Zeit. Viel mehr um die Sicherheit, dass "su" den Wert hat, den es haben soll.

Die Funktionen die nicht auf Acquire oder Release enden haben Acquire+Release Semantik, d.h. auf der sicheren Seite bist du damit auf jeden Fall.
(Auf Intel x86 haben die sogar "full barrier" Semantik, weiss aber nicht ob das z.B. auch für IA64 garantiert ist)


EDIT: hab gerade nachgesene, "full barrier" ist anscheinend für alle Plattformen garantiert:
MSDN schrieb:

InterlockedCompareExchange Function
(...)
This function generates a full memory barrier (or fence) to ensure that memory operations are completed in order.

_________________
"Let there be Licht..." http://lichttools.sourceforge.net/
Sehr cooles ASCII Spiel (leider nicht von mir): ASCII-Scramble - http://www.roskakori.at/ascii/


Zuletzt bearbeitet von hustbaer am 06:56:40 01.08.2010, insgesamt 1-mal bearbeitet
RedPuma
Mitglied

Benutzerprofil
Anmeldungsdatum: 24.01.2008
Beiträge: 165
Beitrag RedPuma Mitglied 11:04:58 01.08.2010   Titel:              Zitieren

Zitat:
Wieso sollte man sich immer alles selbst programmieren?
Etwas selbst zu machen, nur weil es geht, ist meist schlecht. Wenn man es macht um dabei zu lernen, OK. Wenn man es nur macht weil man einen furchtbaren Dickschädel hat, ist es aber fast immer ein Fehler.

* Die Boost.Thread ist quasi-Standard, und wird fast 1:1 in der Form wohl auch im neuen C++ Standard landen
* Die Boost.Thread ist "tried and true", und von Leuten programmiert die ziemlich gut wissen was sie tun
* Die Boost.Thread ist komfortabel zu handhaben, was ich von deiner Klasse nicht sagen würde. Es fehlt z.B. die "Scoped-Lock" Klasse, die die Mutex/CRITICAL_SECTION im Konstruktor lockt und im Destruktor unlockt

Und noch ganz konkret zum Thema warum ich es für eine schlechte Idee halte, sich selbst was zu basteln, was es schon (als allgemein akzeptierte Komponente) fertig gibt...
Die meisten Leute machen bei der Implementierung nicht nur Fehler, sondern gewöhnen sich dabei auch an, bestimmte Dinge nach ihrem eigenen Dickschädel zu machen, und nicht wie sie "alle anderen" machen. Reicht schon wenn etwas was überall "foo" heisst bei dir dann "bar" heisst. Der Effekt der Implementierungsfehler dürfte klar sein. Der Effekt von anderen Namen/Begriffen ist, dass man anfängt ein anderes Vokabular zu verwenden. Und das ist IMO immer ein Nachteil, da es eine unnötige Kommunikations-Barriere darstellt.

Konkret am Beispiel deines Codes:

Implementierungs-Fehler:

* Deine CThreadMutex Klasse ist copy-constructable, dürfte es aber nicht sein

* Deine CThreadMutex Klasse ist assignable, dürfte es aber nicht sein


Quality-Of-Implementation:

* Der Konstruktor verwendet InitializeCriticalSection. InitializeCriticalSection kann fehlschlagen, diesen Umstand aber nicht kommunizieren. -> Lieber InitializeCriticalSectionAndSpinCount verwenden

* Du bietest keine Scoped-Lock Klasse an

* Du bietest keine Try-Lock Funktion an


Du hast natürlich völlig recht wenn es dabei um einen industriellen Maßstab geht. Dass meine Wrapper-Klasse vollständig oder besser als die boost-library ist habe ich nicht behauptet, dass sie fehlerfrei ist oder sonstiges auch nicht. Die sollte sowieso nur ein Beispiel sein wie einfach man sowas basteln kann. Bei der Einfachheit hätte ich z.B. gar keine Wrapper-Klasse genommen, jedoch kann ich bei meiner Version wie gesagt noch zwischen Mutex und Crit Section umschalten.

Dass die boost library inzwischen standard ist ist mir auch klar, nur finde ich eher dass man bei solch "kleinen" Problemen keine solch "riesige" Bibliothek heranziehen muss, und sich mit deren Benutzung auseinandersetzen muss. Vor allem in dem Hinblick dass sich der Eröffner dieses Threads schon zahlreich mit den Windows-Synchronisations-Objekten vertraut gemacht hat, und das Einarbeiten in boost::thread unnötig währe.

/Edit: Ach ja, der Name CThreadMutex stammt daher dass mich Visual Studio 10 keine Klasse mit dem Namen CMutex erstellen ließ. Also zumindest der Dialog nach der Schaltfläche "Klasse hinzufügen" verweigerte mir dies. Da ich mir dachte das dies vielleicht aus gutem Grund geschieht habe ich meine Klasse dann nicht einfach über den Texteditor erstellt sondern umbenannt.


Zuletzt bearbeitet von RedPuma am 11:09:30 01.08.2010, insgesamt 2-mal bearbeitet
FrEEzE2046
Mitglied

Benutzerprofil
Anmeldungsdatum: 03.12.2008
Beiträge: 786
Beitrag FrEEzE2046 Mitglied 12:27:17 01.08.2010   Titel:              Zitieren

RedPuma schrieb:
Also zumindest der Dialog nach der Schaltfläche "Klasse hinzufügen" verweigerte mir dies. Da ich mir dachte das dies vielleicht aus gutem Grund geschieht habe ich meine Klasse dann nicht einfach über den Texteditor erstellt sondern umbenannt.


Das benutzt jemand? Das vorangestellte "C" sieht man sonst vor allem beim - mir zu widerem - MFC.

Desweiteren möchte ich die boost Library nicht heranziehen. Das wäre auch unnötig, da es sich hier um eine relativ kleine Problematik handelt für die man prinzipiell nicht mal eine Thread-Wrapper Klasse benötigt.
FrEEzE2046
Mitglied

Benutzerprofil
Anmeldungsdatum: 03.12.2008
Beiträge: 786
Beitrag FrEEzE2046 Mitglied 14:45:42 01.08.2010   Titel:              Zitieren

Evtl. habe ich irgendetwas - event objects betreffend - noch nicht richtig verstanden. Kann mir jemand sagen warum ich hier beim Aufruf von WaitForSingleObject() in einer Endlosschleife hängen bleibe?

C/C++ 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
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
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
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
#include <iostream>
#include
<process.h>
#include
<Windows.h>

using namespace std;


unsigned __stdcall ThreadProc(void*);


int main()
{
    unsigned threadID = 0;
    DWORD     threadExitCode = 0;
   
    HANDLE hEvent = CreateEvent(nullptr, TRUE, FALSE, nullptr);
    HANDLE hThread = reinterpret_cast<HANDLE>(_beginthreadex(
        nullptr,
        0,
        ThreadProc,
        &hEvent,
        0,
        &threadID
    ));
   
    for( ;; )
    {
        while( WaitForSingleObject(hEvent, 1) == WAIT_TIMEOUT ) {Sleep(1);}
        cout << "time elapsed" << endl;
        Sleep(1);

        if( !WaitForSingleObject(hThread, 1) /*WAIT_OBJECT_0*/ )
            break;
    }

    GetExitCodeThread(hThread, &threadExitCode);
    cout << threadExitCode << endl;

    CloseHandle(hEvent);
    CloseHandle(hThread);
    return 0;
}


unsigned __stdcall ThreadProc(void* param)
{
    HANDLE  hEvent = static_cast<HANDLE>(param);

    for( int i = 0; i < 10; ++i )
    {
        for( int j = 0; j < 1 << 31 - 1; ++j );
        PulseEvent(hEvent);
        Sleep(1);
    }

    _endthreadex(4711);
    return 0;
}
C/C++ 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
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
#include <iostream>
#include
<process.h>
#include
<Windows.h>

using namespace std;


unsigned __stdcall ThreadProc(void*);


int main()
{
unsigned threadID = 0;
DWORD threadExitCode = 0;

HANDLE hEvent = CreateEvent(nullptr, TRUE, FALSE, nullptr);
HANDLE hThread = reinterpret_cast<HANDLE>(_beginthreadex(
nullptr,
0,
ThreadProc,
&hEvent,
0,
&threadID
));

for( ;; )
{
while( WaitForSingleObject(hEvent, 1) == WAIT_TIMEOUT ) {Sleep(1);}
cout << "time elapsed" << endl;
Sleep(1);

if( !WaitForSingleObject(hThread, 1) /*WAIT_OBJECT_0*/ )
break;
}

GetExitCodeThread(hThread, &threadExitCode);
cout << threadExitCode << endl;

CloseHandle(hEvent);
CloseHandle(hThread);
return 0;
}


unsigned __stdcall ThreadProc(void* param)
{
HANDLE hEvent = static_cast<HANDLE>(param);

for( int i = 0; i < 10; ++i )
{
for( int j = 0; j < 1 << 31 - 1; ++j );
PulseEvent(hEvent);
Sleep(1);
}

_endthreadex(4711);
return 0;
}
C/C++ 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
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
#include <iostream>
#include
<process.h>
#include
<Windows.h>

using namespace std;


unsigned __stdcall ThreadProc(void*);


int main()
{
    unsigned threadID = 0;
    DWORD     threadExitCode = 0;
   
    HANDLE hEvent = CreateEvent(nullptr, TRUE, FALSE, nullptr);
    HANDLE hThread = reinterpret_cast<HANDLE>(_beginthreadex(
        nullptr,
        0,
        ThreadProc,
        &hEvent,
        0,
        &threadID
    ));
   
    for( ;; )
    {
        while( WaitForSingleObject(hEvent, 1) == WAIT_TIMEOUT ) {Sleep(1);}
        cout << "time elapsed" << endl;
        Sleep(1);

        if( !WaitForSingleObject(hThread, 1) /*WAIT_OBJECT_0*/ )
            break;
    }

    GetExitCodeThread(hThread, &threadExitCode);
    cout << threadExitCode << endl;

    CloseHandle(hEvent);
    CloseHandle(hThread);
    return 0;
}


unsigned __stdcall ThreadProc(void* param)
{
    HANDLE  hEvent = static_cast<HANDLE>(param);

    for( int i = 0; i < 10; ++i )
    {
        for( int j = 0; j < 1 << 31 - 1; ++j );
        PulseEvent(hEvent);
        Sleep(1);
    }

    _endthreadex(4711);
    return 0;
}


Zuletzt bearbeitet von FrEEzE2046 am 15:39:23 01.08.2010, insgesamt 2-mal bearbeitet
FrEEzE2046
Mitglied

Benutzerprofil
Anmeldungsdatum: 03.12.2008
Beiträge: 786
Beitrag FrEEzE2046 Mitglied 15:42:32 01.08.2010   Titel:              Zitieren

Man sollte dazu sagen, dass ich heute seit Freitag krank bin. Anders kann ich mir diesen lächerlichen Fehler auch nicht erklären. In Zeile 47 muss es natürlich so aussehen:
C/C++ Code:
HANDLE hEvent = *static_cast<HANDLE*>(param);
C/C++ Code:
HANDLE hEvent = *static_cast<HANDLE*>(param);
C/C++ Code:
HANDLE hEvent = *static_cast<HANDLE*>(param);


Jetzt funktioniert das auch.
FrEEzE2046
Mitglied

Benutzerprofil
Anmeldungsdatum: 03.12.2008
Beiträge: 786
Beitrag FrEEzE2046 Mitglied 20:17:36 11.08.2010   Titel:              Zitieren

Ich hole diesen Thread nochmal vor. Wie kann ich denn eine Variable auf die gleichzeitig geschrieben werden könnte sicher lesen?

Ich nutze in meinem Code generell die Interlocked-Funktionen wenn möglich. Ich könnte natürlich mit InterlockedAnd oder InterlockedCompareExchange operieren, die mir jeweils den "alten" Wert zurückliefern, allerdings möchte ich weder austauschen noch konjugieren.

Kennt jemand eine andere Möglichkeit?
hustbaer
Mitglied

Benutzerprofil
Anmeldungsdatum: 27.10.2006
Beiträge: 13522
Beitrag hustbaer Mitglied 20:50:18 11.08.2010   Titel:              Zitieren

InterlockedCompareExchange(&v, 0, 0) geht schön, bzw. natürlich auch allgemein InterlockedCompareExchange(&v, X, X)

Mit MSVC (ab Version 8 aka 2005) + x86 reicht allerdings einfach ein Lesezugriff auf eine volatile Variable.

Bzw. glaube ich sogar ein normaler Lesezugriff wenn man ihn mit zwei _ReadWriteBarrier() Aufrufen einklammert.

EDIT:

MSDN schrieb:

Microsoft Specific

Objects declared as volatile are not used in certain optimizations because their values can change at any time. The system always reads the current value of a volatile object at the point it is requested, even if a previous instruction asked for a value from the same object. Also, the value of the object is written immediately on assignment.

Also, when optimizing, the compiler must maintain ordering among references to volatile objects as well as references to other global objects. In particular,

A write to a volatile object (volatile write) has Release semantics; a reference to a global or static object that occurs before a write to a volatile object in the instruction sequence will occur before that volatile write in the compiled binary.

A read of a volatile object (volatile read) has Acquire semantics; a reference to a global or static object that occurs after a read of volatile memory in the instruction sequence will occur after that volatile read in the compiled binary.

This allows volatile objects to be used for memory locks and releases in multithreaded applications.

Note

Although the processor will not reorder un-cacheable memory accesses, un-cacheable variables must be volatile to guarantee that the compiler will not change memory order.

End Microsoft Specific

_________________
"Let there be Licht..." http://lichttools.sourceforge.net/
Sehr cooles ASCII Spiel (leider nicht von mir): ASCII-Scramble - http://www.roskakori.at/ascii/


Zuletzt bearbeitet von hustbaer am 20:54:07 11.08.2010, insgesamt 2-mal bearbeitet
Patrick Star
Unregistrierter




Beitrag Patrick Star Unregistrierter 14:42:52 12.08.2010   Titel:              Zitieren

Ich begreif's nicht.
Jonny boy
Unregistrierter




Beitrag Jonny boy Unregistrierter 14:58:26 12.08.2010   Titel:              Zitieren

Ich schon lang nicht mehr ...
Andreas XXL
Mitglied

Benutzerprofil
Anmeldungsdatum: 12.01.2004
Beiträge: 915
Beitrag Andreas XXL Mitglied 20:56:32 12.08.2010   Titel:              Zitieren

Ich schlage diesen Beitrag für die FAQ vor!

(Wird zwar nicht so häufig gefragt, sind aber recht vernünftige Beiträge)
xor
Mitglied

Benutzerprofil
Anmeldungsdatum: 16.03.2010
Beiträge: 58
Beitrag xor Mitglied 23:53:37 12.08.2010   Titel:              Zitieren

Falls es um Geschwindigkeit geht, möchte ich noch die intrinsics des MSVC einbringen. So laufen die Operationen nicht als WinAPI Aufruf, sonder werden direkt als atomare CPU-Anweisung in den Code eingefügt. Und dass geht dann noch schneller als über die API.
Hab mir dafür mittels Makros die Funktionen umdefiniert ... hier ein Auszug:

C/C++ Code:
1
2
3
4
5
6
7
8
9
1
2
3
4
5
6
7
8
9
#include "windows.h"

#if
defined(CPP_MSVC05) && !defined(WINCE)
#  include
"intrin.h"
#  define
InterlockedExchange         _InterlockedExchange
#  define
InterlockedCompareExchange  _InterlockedCompareExchange
#  define
InterlockedIncrement        _InterlockedIncrement
#  define
InterlockedDecrement        _InterlockedDecrement
#endif
C/C++ Code:
1
2
3
4
5
6
7
8
9
#include "windows.h"

#if
defined(CPP_MSVC05) && !defined(WINCE)
# include
"intrin.h"
# define
InterlockedExchange _InterlockedExchange
# define
InterlockedCompareExchange _InterlockedCompareExchange
# define
InterlockedIncrement _InterlockedIncrement
# define
InterlockedDecrement _InterlockedDecrement
#endif
C/C++ Code:
1
2
3
4
5
6
7
8
9
#include "windows.h"

#if
defined(CPP_MSVC05) && !defined(WINCE)
#  include
"intrin.h"
#  define
InterlockedExchange         _InterlockedExchange
#  define
InterlockedCompareExchange  _InterlockedCompareExchange
#  define
InterlockedIncrement        _InterlockedIncrement
#  define
InterlockedDecrement        _InterlockedDecrement
#endif

lg XOR
hustbaer
Mitglied

Benutzerprofil
Anmeldungsdatum: 27.10.2006
Beiträge: 13522
Beitrag hustbaer Mitglied 02:46:53 13.08.2010   Titel:              Zitieren

defined(CPP_MSVC05) - huch? :confused:

ich mach immer defined(_MSC_VER) && (_MSC_VER >= ...)

_________________
"Let there be Licht..." http://lichttools.sourceforge.net/
Sehr cooles ASCII Spiel (leider nicht von mir): ASCII-Scramble - http://www.roskakori.at/ascii/
C/C++ Forum :: FAQ - WinAPI ::  Critical Section vs. Mutex Object vs. Interlocked Access   Auf Beitrag antworten

Zeige alle Beiträge auf einer Seite




Nächstes Thema anzeigen
Vorheriges Thema anzeigen
Sie können keine Beiträge in dieses Forum schreiben.
Sie können auf Beiträge in diesem Forum nicht antworten.
Sie können Ihre Beiträge in diesem Forum nicht bearbeiten.
Sie können Ihre Beiträge in diesem Forum nicht löschen.
Sie können an Umfragen in diesem Forum nicht mitmachen.

Powered by phpBB © 2001, 2002 phpBB Group :: FI Theme

c++.de ist Teilnehmer des Partnerprogramms von Amazon Europe S.à.r.l. und Partner des Werbeprogramms, das zur Bereitstellung eines Mediums für Websites konzipiert wurde, mittels dessen durch die Platzierung von Werbeanzeigen und Links zu amazon.de Werbekostenerstattung verdient werden kann.

Die Vervielfältigung der auf den Seiten www.c-plusplus.de, www.c-plusplus.info, www.c-sar.de, www.c-plusplus.net und www.baeckmann.de enthaltenen Informationen ohne eine schriftliche Genehmigung des Seitenbetreibers ist untersagt (vgl. §4 Urheberrechtsgesetz). Die Nutzung und Änderung der vorgestellten Strukturen und Verfahren in privaten und kommerziellen Softwareanwendungen ist ausdrücklich erlaubt, soweit keine Rechte Dritter verletzt werden. Der Seitenbetreiber übernimmt keine Gewähr für die Funktion einzelner Beiträge oder Programmfragmente, insbesondere übernimmt er keine Haftung für eventuelle aus dem Gebrauch entstehenden Folgeschäden.