Autor |
Beitrag |
DelphMan
      
Beiträge: 57
|
Verfasst: Fr 17.11.06 16:33
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
      
Beiträge: 2145
WinXP Prof SP2, Ubuntu 9.04
C/C++(Code::Blocks, VS.NET),A51(Keil),Object Pascal(D2005PE, Turbo Delphi Explorer) C# (VS 2008 Express)
|
Verfasst: Fr 17.11.06 16:53
Geht das Hauptprogramm auch in die CriticalSection?
_________________ Programmers never die, they just GOSUB without RETURN
|
|
Udontknow
      
Beiträge: 2596
Win7
D2006 WIN32, .NET (C#)
|
Verfasst: Fr 17.11.06 17:24
Es ist aber dasselbe CriticalSection-Objekt, ja, und nicht ein separat erstelltes?
Cu,
Udontknow
|
|
DelphMan 
      
Beiträge: 57
|
Verfasst: 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
      
Beiträge: 4613
Erhaltene Danke: 24
XP home, prof
Delphi 2009 Prof,
|
Verfasst: 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 ?
_________________ In the beginning was the word.
And the word was content-type: text/plain.
|
|
alzaimar
      
Beiträge: 2889
Erhaltene Danke: 13
W2000, XP
D6E, BDS2006A, DevExpress
|
Verfasst: 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
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; |
_________________ Na denn, dann. Bis dann, denn.
|
|
DelphMan 
      
Beiträge: 57
|
Verfasst: 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 
      
Beiträge: 57
|
Verfasst: Fr 17.11.06 23:10
alzaimar
Danke für den Code!
|
|
Udontknow
      
Beiträge: 2596
Win7
D2006 WIN32, .NET (C#)
|
Verfasst: 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
      
Beiträge: 2684
Erhaltene Danke: 32
|
Verfasst: 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.
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 
      
Beiträge: 57
|
Verfasst: 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
      
Beiträge: 2684
Erhaltene Danke: 32
|
Verfasst: 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
      
Beiträge: 2889
Erhaltene Danke: 13
W2000, XP
D6E, BDS2006A, DevExpress
|
Verfasst: So 19.11.06 20:28
_________________ Na denn, dann. Bis dann, denn.
|
|
|