Autor Beitrag
DelphMan
ontopic starontopic starontopic starontopic starontopic starontopic starontopic starontopic star
Beiträge: 57



BeitragVerfasst: 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:

ausblenden 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
ontopic starontopic starontopic starontopic starontopic starontopic starhalf ontopic starofftopic star
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)
BeitragVerfasst: Fr 17.11.06 16:53 
Geht das Hauptprogramm auch in die CriticalSection?

_________________
Programmers never die, they just GOSUB without RETURN
Udontknow
ontopic starontopic starontopic starontopic starontopic starontopic starontopic starhalf ontopic star
Beiträge: 2596

Win7
D2006 WIN32, .NET (C#)
BeitragVerfasst: Fr 17.11.06 17:24 
Es ist aber dasselbe CriticalSection-Objekt, ja, und nicht ein separat erstelltes? :wink:

Cu,
Udontknow
DelphMan Threadstarter
ontopic starontopic starontopic starontopic starontopic starontopic starontopic starontopic star
Beiträge: 57



BeitragVerfasst: 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
ontopic starontopic starontopic starontopic starontopic starontopic starhalf ontopic starofftopic star
Beiträge: 4613
Erhaltene Danke: 24

XP home, prof
Delphi 2009 Prof,
BeitragVerfasst: Fr 17.11.06 21:59 
irgendwie kommt mir das komisch vor dein Quelltext. Mach es doch mal so:
ausblenden 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
ontopic starontopic starontopic starontopic starontopic starontopic starontopic starofftopic star
Beiträge: 2889
Erhaltene Danke: 13

W2000, XP
D6E, BDS2006A, DevExpress
BeitragVerfasst: 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
ausblenden volle Höhe 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;
...
{ TilasCriticalSection }

constructor TilasCriticalSection.Create;
begin
  fSemaphore := CreateSemaphore(nil11nil);
end;

destructor TilasCriticalSection.Destroy;
begin
  closeHandle(fSemaphore);
end;

procedure TilasCriticalSection.Enter;
begin
  WaitForSingleObject(fSemaphore, INFINITE)
end;

procedure TilasCriticalSection.Leave;
begin
  ReleaseSemaphore(fSemaphore, 1nil); // Inc fSemaphore
end;

_________________
Na denn, dann. Bis dann, denn.
DelphMan Threadstarter
ontopic starontopic starontopic starontopic starontopic starontopic starontopic starontopic star
Beiträge: 57



BeitragVerfasst: Fr 17.11.06 22:53 
user profile iconmatze 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 Threadstarter
ontopic starontopic starontopic starontopic starontopic starontopic starontopic starontopic star
Beiträge: 57



BeitragVerfasst: Fr 17.11.06 23:10 
user profile iconalzaimar
Danke für den Code!
Udontknow
ontopic starontopic starontopic starontopic starontopic starontopic starontopic starhalf ontopic star
Beiträge: 2596

Win7
D2006 WIN32, .NET (C#)
BeitragVerfasst: 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
ontopic starontopic starontopic starontopic starontopic starontopic starontopic starhalf ontopic star
Beiträge: 2684
Erhaltene Danke: 32



BeitragVerfasst: 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.

ausblenden volle Höhe 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); overloadmessage UM_Log;
  end;

var
  Form1: TForm1;

implementation

{$R *.dfm}

{ TMyThread }

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:
ausblenden 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:
ausblenden 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 Threadstarter
ontopic starontopic starontopic starontopic starontopic starontopic starontopic starontopic star
Beiträge: 57



BeitragVerfasst: So 19.11.06 16:53 
user profile icondelfiphan

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
ontopic starontopic starontopic starontopic starontopic starontopic starontopic starhalf ontopic star
Beiträge: 2684
Erhaltene Danke: 32



BeitragVerfasst: 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
ontopic starontopic starontopic starontopic starontopic starontopic starontopic starofftopic star
Beiträge: 2889
Erhaltene Danke: 13

W2000, XP
D6E, BDS2006A, DevExpress
BeitragVerfasst: 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.



user profile iconUdontknow hat folgendes geschrieben:
@alzaimar: Du meinst "prozessübergreifend", nicht? Eine Criticalsection, die nicht threadübergreifend funkioniert, ist ansonsten ihres Daseinszweckes beraubt... ;-)

:oops:

_________________
Na denn, dann. Bis dann, denn.