Du bist nicht angemeldet.

Stilllegung des Forums
Das Forum wurde am 05.06.2023 nach über 20 Jahren stillgelegt (weitere Informationen und ein kleiner Rückblick).
Registrierungen, Anmeldungen und Postings sind nicht mehr möglich. Öffentliche Inhalte sind weiterhin zugänglich.
Das Team von spieleprogrammierer.de bedankt sich bei der Community für die vielen schönen Jahre.
Wenn du eine deutschsprachige Spieleentwickler-Community suchst, schau doch mal im Discord und auf ZFX vorbei!

Werbeanzeige

dot

Supermoderator

Beiträge: 9 757

Wohnort: Graz

  • Private Nachricht senden

11

10.09.2010, 14:31

Ok, ich denke du hast hier noch ein kleines Verständnisproblem was CS angeht: Eine CS macht überhaupt erst dann Sinn wenn mehrere Threads dasselbe CS Objekt benutzen! Deine Klasse ist im Moment ein Mittelding zwischen einem Wrapper für eine CRITICAL_SECTION und einem Lock Objekt, wobei die Sache mit dem Lock Objekt so nicht funktionieren kann.
Vielleicht ein kleines Beispiel wie man eine CRITICAL_SECTION verwenden würde:

C-/C++-Quelltext

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
CRITICAL_SECTION cs;

std::list<int> my_list;


  //...

  InitializeCriticalSection(&cs);

  // ...

void thread1()
{
  while (true)
  {
    EnterCriticalSection(&cs);

    my_list.push_back(23);

    LeaveCriticalSection(&cs);
  }
}

void thread2()
{
  while(true)
  {
    EnterCriticalSection(&cs);

    my_list.push_back(42);

    LeaveCriticalSection(&cs);
  }
}


Die Idee ist dass EnterCriticalSection() den aufrufenden Thread solange blockiert bis die CriticalSection frei ist, d.h. niemals mehr als ein Thread gleichzeitig in die CriticalSection hinein kann sodass immer nur ein Thread die Liste verändern kann (würden zwei Threads gleichzeitig ein push_back() machen dann hätte man eine schöne Race Condition). Die CS dient hier also als Lock für die Liste. Wenn thread1 gerade in der CS drinnen ist, und thread2 nun EnterCriticalSection() aufruft dann würde EnterCriticalSection() in thread2 solange warten bis thread1 LeaveCriticalSection() aufruft. Wichtig ist aber dass beide Threads natürlich dasselbe Lock, also dasselbe CRITICAL_SECTION Objekt verwenden müssen.

Was dein Konstruktor jetzt aber tut ist: Er nimmt ein bestehendes CRITICAL_SECTION Objekt und kopiert es und Initialisiert die Kopie dann neu. D.h. das kopieren ist also total sinnlos und das Ergebnis ist am Ende so als hätte man einfach den anderen Konstruktor benutzt.

Die Frage nach dem "Status" einer CriticalSection ist gleichbedeutend mit der Frage ob der aktuelle Thread blockieren würde wenn er in die CriticalSection eintreten würde (d.h. ob gerade ein anderer Thread in der CriticalSection ist). Dazu gibt es die Funktion TryEnterCriticalSection(). Deine mode Variable ist damit also überflüssig, noch dazu würde sie unter Umständen falsche Ergebnise liefern (z.B. angenommen ein Thread hat gerade EnterCriticalSection() erfolgreich ausgeführt und wird vom Scheduler unterbrochen bevor er mode auf true setzen kann. Ein nächster Thread kommt dran und will mode prüfen um zu entscheiden ob er in die CS eintreten soll. mode ist aber noch auf false und der Thread macht darum ebenfalls EnterCriticalSection() blockier nun aber da die CS bereits belegt ist).

Was die Sache mit dem Lockable angeht so hast du natürlich ein Argument. Allerdings eines das ich hinterfragen möchte: Im Prinzip haben nur Mutex und CriticalSection das gleiche Interface (Lock + Unlock) wobei zusätzlich beide in grundverschiedenen Kontexten verwendet werden (Interprozess vs. Interthread synchronisation). In welcher Hinsicht willst du hier von Polymorphie profitieren? Natürlich kannst du so Mutexe und CriticalSections auf abstrakter Ebene gleich behandeln, allerdings ist das praktisch genauso sinnvoll wie eine gemeinsame Basisklasse für Vögel und Flugzeuge weil beide fliegen können...

Das mit dem Unique ist natürlich ein brauchbarer Ansatz, nur würde ich hier die Frage stellen ob wirklich jedes Lockable immer auch Unique ist oder z.B. ein Mutex wenn dann nicht besser Lockable und Unique wäre.

Sorry wenn ich so viele Fragen stelle nur ich fange jetzt erst mit mehreren Threads an :S

Versteh das nicht falsch, hier ist dir niemand böse wenn du Fragen stellst und was ich sage ist nicht als abwertende Kritik gemeint. Ich versuch nur ein wenig mit dir über deine Ansätze zu diskutieren mit dem Ziel dass wir vielleicht beide was dabei lernen ;)

Dieser Beitrag wurde bereits 7 mal editiert, zuletzt von »dot« (10.09.2010, 15:33)


Zeus

Frischling

  • »Zeus« ist der Autor dieses Themas

Beiträge: 83

Beruf: Schule

  • Private Nachricht senden

12

10.09.2010, 20:13

Ok, vielen Dank für die ausführliche Erklärung. Hilft mir wirklich weiter da ich im Moment nicht wirklich weis was wo wie Sache ist.

Also müsste ich damit der Constructor Sinn ergibt eine Referenz (Pointer) auf mein CS übergeben ? [Das bringt mich dann aber wieder zum nem GarbageCollector den ich im Moment (auch) schreibe. Ohne den ist nicht gewährleistet das der Lock auch mit einem gültigen CS arbeitet]

Das mit dem mode da hast du Recht. Es macht keinen Sinn. Ich habe nicht bedacht das mein Thread ja zu beliebiger Zeit unterbrochen werden kann.

Ja OK, überzeugt. Lockable gibt auch kein Sinn. Bin ja noch am einarbeiten und ich hab in naiver Annahme das sie ähnlich sind gedacht ich kann das machen ... Ich habe das als Idee aus einer PDF übernommen die ich gerade lese die sich mit der nicht OS oder Prozessor gebundenen implimentierung von Locks beschäftigt (also POSIX). Da ich aber die Win eigenen verwende ist das knülle.

Hmmm ... könnte man auch machen. Ich bin davon ausgegangen das es sinnfrei ist einen Lock zu kopieren.

-> also Fazit ich kill die Lockable Klasse.

Ne, keine Sorge. Habe ich nicht so verstanden :) finde ich gut wenn du das so siehst.

EDIT:

Habs jetzt so gelöst. Ist das korrekt ?

C-/C++-Quelltext

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
class CriticalSection; 
class CriticalSectionPolicy; 
typedef CriticalSection* LPCriticalSection; 
class CriticalSection 
{ 
friend class CriticalSectionPolicy; 
private: 
CRITICAL_SECTION m_Section; 
public: 
CriticalSection() 
{ 
InitializeCriticalSection(&m_Section); 
} 
~CriticalSection() 
{ 
DeleteCriticalSection(&m_Section); 
} 
}; 
class CriticalSectionPolicy : public Scope::Unique 
{ 
private: 
LPCriticalSection m_Critical;
bool locked;
public: explicit CriticalSectionPolicy(LPCriticalSection section, bool lock = true) 
{ 
 this->m_Critical = section;if(lock)this->lock(); 
} 
public: ~CriticalSectionPolicy() 
{ 
 if(this->locked)this->unlock(); 
} 
public: void lock() 
{ 
EnterCriticalSection(&(this->m_Critical->m_Section)); 
this->locked = true; 
} 
public: void unlock() 
{ 
LeaveCriticalSection(&(this->m_Critical->m_Section)); 
this->locked = false; 
} 
public: bool TryLock() 
{ 
return TryEnterCriticalSection(&(this->m_Critical->m_Section)); 
} 
};
Ich würde die Welt gerne verbessern, doch Gott gibt mir den Quellcode nicht!

Sprachen: C,C++/CLI,C#,ASM,PHP,Java(-script) ... fürn Anfang auch genug ...

Mein letztes Projekt:

http://www.youtube.com/watch?v=vU14ewcVaXU

Dieser Beitrag wurde bereits 6 mal editiert, zuletzt von »Zeus« (10.09.2010, 22:34)


dot

Supermoderator

Beiträge: 9 757

Wohnort: Graz

  • Private Nachricht senden

13

10.09.2010, 22:51

Nun deine CriticalSectionPolicy ist jetzt ja im Prinzip das scoped_lock Objekt. Und im Prinzip dürfte das so schon funktionieren. Was ich mich noch frage: Warum hat jetzt die Policy die Lock und Unlock Methoden. Soll die Policy nicht einfach im Konstruktor locken und im Destruktor unlocken? Eine CS die bereits gelocked ist nochmal zu locken macht keinen Sinn. Wenn Lock, Unlock und TryLock direkt in der CriticalSection wären dann würdest du dir auch die friend Geschichte sparen. Außerdem finde ich die Bezeichnung CriticalSectionPolicy ein wenig irreführend. Unter Policies versteht man normal ein Konzept aus der generischen Programmierung mit dem deine Klasse eigentlich nichts gemeinsam hat. Warum verwendest du eigentlich einen Pointer auf die CriticalSection? Wäre eine Referenz hier nicht eher angebracht?

Dieser Beitrag wurde bereits 1 mal editiert, zuletzt von »dot« (10.09.2010, 23:00)


Zeus

Frischling

  • »Zeus« ist der Autor dieses Themas

Beiträge: 83

Beruf: Schule

  • Private Nachricht senden

14

11.09.2010, 01:39

Nunja, die lock und unlock habe ich, da ich mir gedacht habe wenn ich ein rießigen Quellcode habe und den CS nicht weiter blockieren will gebe ich ihn schnell per hand frei ... So können andere Threads weiter arbeiten und müssen nicht warten bis das CS sich von aleine auflöst. Oder ich brauche in einer Funktion ein CS mehrfach. So kann ich ihn zwischenrein immer wieder freigeben und andere Threads können in der Zeit an ihre Daten.

Das mit dem dem CS alles zusammen habe ich probiert. Also im Constructor dann das Initialisiren von der CS und im Destruktor löschen. Dabei habe ich aber das Problem gehabt das gelegentlich das die CS doppelt Initialisirt oder gelöscht wurde (also von zwei Threads) das hat wieder zum gleichen Fehler geführt wie wenn ich es gleich weg lasse.

Ja stimmt ansich. Wäre besser. Ich arbeite (warum auch immer) gerne mit Pointern. Deshalb hat sich das eingeschlichen das ich nur selten Referenzen nutze.
Ich würde die Welt gerne verbessern, doch Gott gibt mir den Quellcode nicht!

Sprachen: C,C++/CLI,C#,ASM,PHP,Java(-script) ... fürn Anfang auch genug ...

Mein letztes Projekt:

http://www.youtube.com/watch?v=vU14ewcVaXU

dot

Supermoderator

Beiträge: 9 757

Wohnort: Graz

  • Private Nachricht senden

15

11.09.2010, 10:25

Nunja, die lock und unlock habe ich, da ich mir gedacht habe wenn ich ein rießigen Quellcode habe und den CS nicht weiter blockieren will gebe ich ihn schnell per hand frei ... So können andere Threads weiter arbeiten und müssen nicht warten bis das CS sich von aleine auflöst. Oder ich brauche in einer Funktion ein CS mehrfach. So kann ich ihn zwischenrein immer wieder freigeben und andere Threads können in der Zeit an ihre Daten.

"Per Hand freigeben" unterminiert aber den ganzen Sinn der Sache. Außerdem musst du das nicht:

C-/C++-Quelltext

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
void blub()
{
  // ...

  {
    scoped_lock<CriticalSection> lock(cs);
    // ...
  }

  // ...

  {
    scoped_lock<CriticalSection> lock(cs);
    // ...
  }

  // ...
}


Das mit dem dem CS alles zusammen habe ich probiert. Also im Constructor dann das Initialisiren von der CS und im Destruktor löschen. Dabei habe ich aber das Problem gehabt das gelegentlich das die CS doppelt Initialisirt oder gelöscht wurde (also von zwei Threads) das hat wieder zum gleichen Fehler geführt wie wenn ich es gleich weg lasse.

Wie kann das passieren. Das kann doch nur passieren wenn du zwei CS Objekte machst und nicht nur eines für beide Threads!?

Ja stimmt ansich. Wäre besser. Ich arbeite (warum auch immer) gerne mit Pointern. Deshalb hat sich das eingeschlichen das ich nur selten Referenzen nutze.

Pointer sind kein Ersatz für Referenzen und umgekehrt. Auch wenn Pointer und Referenzen gewisse Ähnlichkeiten haben handelt es sich um zwei semantisch komplett unterschiedliche Dinge. Ich würde empfehlen dass du dir mal anschaust was Referenzen genau sind und sie dann auch entsprechend einsetzt.

Dieser Beitrag wurde bereits 1 mal editiert, zuletzt von »dot« (11.09.2010, 10:30)


drakon

Supermoderator

Beiträge: 6 513

Wohnort: Schweiz

Beruf: Entrepreneur

  • Private Nachricht senden

16

11.09.2010, 11:57

Pointer sind kein Ersatz für Referenzen und umgekehrt.

Jain. Mit Pointern ist prinzipiell alles möglich, was mit Referenzen möglich ist. Somit wären Zeiger als Ersatz für Referenzen möglich, aber nicht umgekehrt. :)
Allerdings muss das nicht gut sein. Ich persönlich bevorzuge Referenzen und nicht Zeiger, weil man damit schlichtweg weniger Fehler machen kann (und weniger beachten muss). Und meistens reichen Referenzen völlig aus.

dot

Supermoderator

Beiträge: 9 757

Wohnort: Graz

  • Private Nachricht senden

17

11.09.2010, 19:57

Auch wenn man vielleicht rein was die Funktionalität angeht mit Pointern das Auslangen finden kann gibt es zwischen Pointern und Referenzen wesentliche semantische Unterschiede. Ich sehe jedenfalls hinter Referenzen (Alias für ein Objekt, eine Referenz selbst ist nicht unbedingt ein Objekt) ein grundlegend anderes Konzept als hinter Pointern (eigenständiges Objekt das die Adresse eines anderen Objektes enthält).

Dieser Beitrag wurde bereits 2 mal editiert, zuletzt von »dot« (11.09.2010, 20:10)


drakon

Supermoderator

Beiträge: 6 513

Wohnort: Schweiz

Beruf: Entrepreneur

  • Private Nachricht senden

18

11.09.2010, 22:11

Da stimme ich dir zu. Aber ein Ersatz muss nicht unbedingt äquivalent sein. (Zuckerersatzstoffe sind auch so ziemlich etwas anderes, wie Zucker. :P)
Ich wollte aber keine Detaildiskussion anfangen, sondern einfach betonen wo die Unterschiede sind.

dot

Supermoderator

Beiträge: 9 757

Wohnort: Graz

  • Private Nachricht senden

19

11.09.2010, 22:25

Der Sinn von Zuckerersatzstoffen ist aber der gleiche wie der von Zucker :P

Dieser Beitrag wurde bereits 1 mal editiert, zuletzt von »dot« (11.09.2010, 22:33)


Zeus

Frischling

  • »Zeus« ist der Autor dieses Themas

Beiträge: 83

Beruf: Schule

  • Private Nachricht senden

20

12.09.2010, 03:31

Das mit dem dem CS alles zusammen habe ich probiert. Also im Constructor dann das Initialisiren von der CS und im Destruktor löschen. Dabei habe ich aber das Problem gehabt das gelegentlich das die CS doppelt Initialisirt oder gelöscht wurde (also von zwei Threads) das hat wieder zum gleichen Fehler geführt wie wenn ich es gleich weg lasse.

Wie kann das passieren. Das kann doch nur passieren wenn du zwei CS Objekte machst und nicht nur eines für beide Threads!?




Ich habe, wenn ich ehrlich bin keine Ahnung. Habe das gleiche CS wie oben verwendet nur das ich das INIT und DELETE in Const-/bzw. Destruktor hatte. Hat wieder den gleichen Fehler gegeben wie wenn ich das CS ganz weg lasse. Da er dann gelegentlich zweimal in dirketer Folge (von unterschiedlichen Threads) das INIT verwendet hat. Und dass, das vorherige LOCK platt gemacht hat.


Nunja, die lock und unlock habe ich, da ich mir gedacht habe wenn ich ein rießigen Quellcode habe und den CS nicht weiter blockieren will gebe ich ihn schnell per hand frei ... So können andere Threads weiter arbeiten und müssen nicht warten bis das CS sich von aleine auflöst. Oder ich brauche in einer Funktion ein CS mehrfach. So kann ich ihn zwischenrein immer wieder freigeben und andere Threads können in der Zeit an ihre Daten.

"Per Hand freigeben" unterminiert aber den ganzen Sinn der Sache. Außerdem musst du das nicht:



Habe ich mir gedacht. Aber ich finde es genauso flasch den CS zu blockieren. Und das mit den unterschiedlichen SCOPES ist nunja ... Nicht jeder andere Programmierer bedenkt das. Ich denke noch einmal darüber nach ...

Das mit Referenzen schau ich mir nochmal an :)

Danke für eure dicke Hilfe :)

MFG Zeus
Ich würde die Welt gerne verbessern, doch Gott gibt mir den Quellcode nicht!

Sprachen: C,C++/CLI,C#,ASM,PHP,Java(-script) ... fürn Anfang auch genug ...

Mein letztes Projekt:

http://www.youtube.com/watch?v=vU14ewcVaXU

Werbeanzeige