Entwickler-Ecke
Sonstiges (Delphi) - Threads: Hauptprogramm ignoriert EnterCriticalSection
DelphMan - Fr 17.11.06 16:33
Titel: Threads: Hauptprogramm ignoriert EnterCriticalSection
Hallo,
1 Hauptprogramm mit 100 Threads läuft.
Jeder Thread ruft regelmäßig eine Funktion auf, die ihm eine eindeutige Nummer gibt,
das geht so:
Delphi-Quelltext
1: 2: 3:
| EnterCriticalSection(CriticalSection); inc(i); LeaveCriticalSection(CriticalSection); |
Und funktioniert auch wunderbar, wenn die THREADS diese Funktion aufrufen.
Das Hauptprogramm muß aber unbedingt auch hin und wieder sich eine eindeutige
Nummer ziehen. Dabei habe ich festgestellt, dass es immer die EnterCriticalSection
überqueren darf, selbst wenn ein Thread diese gerade geblockt hat, weil er gerade
"drin" ist. Was kann ich dagegen tun?
Klar, eine Lösung wäre, das Hauptprogramm ebenfalls in einen Thread zu verlagern,
aber der Aufwand :-) Gibt es eine elegante Alternative?
Vielen Dank!
DelphMan
alias5000 - Fr 17.11.06 16:53
Geht das Hauptprogramm auch in die CriticalSection?
Udontknow - Fr 17.11.06 17:24
Es ist aber dasselbe CriticalSection-Objekt, ja, und nicht ein separat erstelltes? :wink:
Cu,
Udontknow
DelphMan - Fr 17.11.06 20:11
Natürlich ist es immer ein und dasselbe CriticalSection-Objekt!
Also das Hauptprogramm kommt immer über die CriticalSection hinweg in den kritischen Bereich und muß nie warten, so als sei es immun gegen die Warte-Vorschrift.
matze - Fr 17.11.06 21:59
irgendwie kommt mir das komisch vor dein Quelltext. Mach es doch mal so:
Delphi-Quelltext
1: 2: 3:
| CriticalSection.Enter; inc(i); CriticalSection.Leave; |
Ich weiß zwar nicht, ob das was bringt, aber bei mir funktioniert das immer. Die Funktionen EnterCritiaclSection() hab ich noch nirgends gesehen !
BZW: Dein Criticalsection object wird ja schon korrekt erstellt, oder ?
alzaimar - Fr 17.11.06 22:44
Eine CriticalSection ist ein Leichtgewicht. Verwende lieber ein Mutex oder eine Semaphore, die sind auch threadübergreifend. Lies Dir mal die OH zu Critical Sections durch. Ich bin neulich auch drüber gestolpert, schließlich hatte ich die Faxen dicke und hab's so gemacht
Delphi-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:
| Type TilasCriticalSection = class private fSemaphore: THandle; public constructor Create; destructor Destroy; override; procedure Enter; procedure Leave; end; ...
constructor TilasCriticalSection.Create; begin fSemaphore := CreateSemaphore(nil, 1, 1, nil); end;
destructor TilasCriticalSection.Destroy; begin closeHandle(fSemaphore); end;
procedure TilasCriticalSection.Enter; begin WaitForSingleObject(fSemaphore, INFINITE) end;
procedure TilasCriticalSection.Leave; begin ReleaseSemaphore(fSemaphore, 1, nil); end; |
DelphMan - Fr 17.11.06 22:53
matze hat folgendes geschrieben: |
BZW: Dein Criticalsection object wird ja schon korrekt erstellt, oder ? |
Ja, denn wenn ein Thread dort reinwill und ein anderer ist drin (bzw. ich mache
mutwillig ein Enter ohne Leave), dann geht vom thread aus garnichts mehr.
DelphMan - Fr 17.11.06 23:10
alzaimar
Danke für den Code!
Udontknow - So 19.11.06 13:41
@alzaimar: Du meinst "prozessübergreifend", nicht? Eine Criticalsection, die nicht threadübergreifend funkioniert, ist ansonsten ihres Daseinszweckes beraubt... ;-)
Cu,
Udontknow
delfiphan - So 19.11.06 14:28
Ich sehe jetzt nicht ein, wieso die CriticalSection nicht funktionieren sollte.
Ich hab auch mal einen Test geschrieben und alles scheint im Butter zu sein.
Delphi-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: 47: 48: 49: 50: 51: 52: 53: 54: 55: 56: 57: 58: 59: 60: 61: 62: 63: 64: 65: 66: 67: 68: 69: 70: 71: 72: 73: 74: 75: 76: 77: 78: 79: 80: 81: 82: 83: 84: 85: 86: 87: 88: 89: 90: 91: 92: 93: 94: 95: 96: 97: 98: 99: 100: 101: 102: 103: 104: 105:
| unit Unit1;
interface
uses Windows, Messages, SysUtils, Variants, Classes, Graphics, Controls, Forms, Dialogs, SyncObjs, ExtCtrls, StdCtrls;
const UM_Log = WM_USER + 1;
type TMyThread = class(TThread) FCriticalSection: TCriticalSection; FHandle: THandle; FResource: PByte; public procedure Execute; override; constructor Create(Handle: THandle; Resource: PByte; CriticalSection: TCriticalSection); end;
TForm1 = class(TForm) Timer1: TTimer; Memo1: TMemo; procedure Timer1Timer(Sender: TObject); procedure FormDestroy(Sender: TObject); procedure FormCreate(Sender: TObject); private MyThread: TMyThread; CS: TCriticalSection; Resource: Integer; procedure Error(var Msg: TMessage); overload; message UM_Log; end;
var Form1: TForm1;
implementation
{$R *.dfm}
constructor TMyThread.Create(Handle: THandle; Resource: PByte; CriticalSection: TCriticalSection); begin inherited Create(false); FHandle := Handle; FCriticalSection := CriticalSection; FResource := Resource; end;
procedure TMyThread.Execute; var C1, C2, F: Int64; begin repeat QueryPerformanceFrequency(F); QueryPerformanceCounter(C1); FCriticalSection.Enter; QueryPerformanceCounter(C2); PostMessage(FHandle, UM_Log, Round(1000*(C2-C1)/F), FResource^); FResource^ := 1; sleep(Random(500)); FResource^ := 0; FCriticalSection.Leave; sleep(Random(500)); until Terminated; end;
procedure TForm1.Error(var Msg: TMessage); begin Memo1.Lines.Add(Format('Thread: Waited %dms for resource. Resource was %d (should be 0). ',[Msg.WParam,Msg.LParam])); end;
procedure TForm1.FormCreate(Sender: TObject); begin CS := TCriticalSection.Create; MyThread := TMyThread.Create(Handle, @Resource, CS); end;
procedure TForm1.FormDestroy(Sender: TObject); begin MyThread.Terminate; MyThread.WaitFor; MyThread.Free; CS.Free; end;
procedure TForm1.Timer1Timer(Sender: TObject); var C1, C2, F: Int64; begin QueryPerformanceFrequency(F); QueryPerformanceCounter(C1); CS.Enter; QueryPerformanceCounter(C2); Memo1.Lines.Add(Format('Mainthread: Waited %dms for resource. Resource was %d (should be 0). ',[Round(1000*(C2-C1)/F),Resource])); Resource := 1; sleep(Random(500)); Resource := 0; CS.Leave; sleep(Random(500)); end;
end. |
Ausgabe:
Quelltext
1: 2: 3: 4: 5: 6: 7: 8: 9: 10: 11:
| Mainthread: Waited 350ms for resource. Resource was 0 (should be 0). Thread: Waited 36ms for resource. Resource was 0 (should be 0). Mainthread: Waited 174ms for resource. Resource was 0 (should be 0). Thread: Waited 109ms for resource. Resource was 0 (should be 0). Mainthread: Waited 0ms for resource. Resource was 0 (should be 0). Thread: Waited 0ms for resource. Resource was 0 (should be 0). Thread: Waited 0ms for resource. Resource was 0 (should be 0). Mainthread: Waited 230ms for resource. Resource was 0 (should be 0). Thread: Waited 205ms for resource. Resource was 0 (should be 0). Mainthread: Waited 184ms for resource. Resource was 0 (should be 0). Thread: Waited 0ms for resource. Resource was 0 (should be 0). |
Ohne Critial Sections würde das ganze so aussehen:
Quelltext
1: 2: 3: 4: 5: 6: 7: 8: 9: 10:
| Mainthread: Waited 0ms for resource. Resource was 0 (should be 0). Thread: Waited 0ms for resource. Resource was 1 (should be 0). Thread: Waited 0ms for resource. Resource was 0 (should be 0). Mainthread: Waited 0ms for resource. Resource was 1 (should be 0). Mainthread: Waited 0ms for resource. Resource was 0 (should be 0). Thread: Waited 0ms for resource. Resource was 1 (should be 0). Thread: Waited 0ms for resource. Resource was 0 (should be 0). Mainthread: Waited 0ms for resource. Resource was 0 (should be 0). Thread: Waited 0ms for resource. Resource was 1 (should be 0). Thread: Waited 0ms for resource. Resource was 0 (should be 0). |
DelphMan - So 19.11.06 16:53
delfiphan
Ich werde selbstverständlich die Anregungen aus dem Thread hier nutzen, aber der Vollständigkeit halber:
Jetzt gibt es nur noch 3 Möglichkeiten, warum bei mir der Hauptthread immun ist:
Entweder weil:
1) Ich habe EnterCriticalSection(CriticalSection); / LeaveCriticalSection(CriticalSection); benutzt
und/oder
2) Mein Critical-Section initialisiere ich mit InitializeCriticalSection(CriticalSection);
und/oder
3) Ich nutze ein var CriticalSection: TRTLCriticalSection;
delfiphan - So 19.11.06 18:24
Okay. Übrigens: Wenn du nur Werte inkrementierst, tut es auch die Funktion InterlockedIncrement. Die tut genau das, was du brauchst.
alzaimar - So 19.11.06 20:28
delphifan: Zwischen Timer und Thread scheint es keine Probleme zu geben, sehr wohl aber zwischen Hauptprogramm und Thread: Ersetze den Zugriff per Timer durch einen Zugriff per Buttonclick und dann solltest Du bemerken, wie der Zugriff per Buttonclick die Criticalsection ignoriert.
Udontknow hat folgendes geschrieben: |
@alzaimar: Du meinst "prozessübergreifend", nicht? Eine Criticalsection, die nicht threadübergreifend funkioniert, ist ansonsten ihres Daseinszweckes beraubt... ;-)
|
:oops:
Entwickler-Ecke.de based on phpBB
Copyright 2002 - 2011 by Tino Teuber, Copyright 2011 - 2025 by Christian Stelzmann Alle Rechte vorbehalten.
Alle Beiträge stammen von dritten Personen und dürfen geltendes Recht nicht verletzen.
Entwickler-Ecke und die zugehörigen Webseiten distanzieren sich ausdrücklich von Fremdinhalten jeglicher Art!