3

Erstellen einer VCL-Formularanwendung, setzen Sie ein TButton und TMemo auf dem Formular, und schreiben Sie diesen Code in der OnClick-Handler-Taste:Hauptgewinde blockiert paralleles Gewinde?

uses 
    OtlParallel, OtlTaskControl; 

procedure TForm2.btnStartLoopClick(Sender: TObject); 
var 
    starttime: Cardinal; 
    k: Integer; 
begin 
    mmoTest.Lines.Clear; 
    for k := 1 to 50 do 
    mmoTest.Lines.Add('Line ' + IntToStr(k)); 

    starttime := GetTickCount; 
    Parallel.Async(
    procedure 
    var 
     i: Integer; 
    begin 
     for i := 1 to 50 do 
     begin 
     Sleep(100); 
     mmoTest.Lines[i - 1] := mmoTest.Lines[i - 1] + FormatDateTime(' nn:ss:zzz', Now); 
     end; 
    end, 
    Parallel.TaskConfig.SetPriority(TOTLThreadPriority.tpHighest).OnTerminated(
    procedure 
    begin 
     mmoTest.Lines.Add(IntToStr(GetTickCount - starttime) + ' milliseconds'); 
    end)); 
end; 

nun das Programm ausführen und diesen Test machen:

  1. Klicken Sie auf die Schaltfläche, warten Sie einfach, bis die Schleife abgeschlossen ist, und sehen Sie sich die Zeit an, die in der letzten Zeile des Memos angezeigt wird: Sie sollte ungefähr 5300 Millisekunden betragen.

  2. Klicken Sie erneut auf die Schaltfläche, klicken und halten Sie die Titelleiste des Formulars und verschieben Sie das Formular schnell, bis die Schleife beendet ist. Sehen Sie sich noch einmal die letzte Zeile des Memos an: In meinen Tests lag die Zeit bei über 7000 Millisekunden. Offensichtlich blockiert der Hauptfaden den parallelen Faden!

Wie also kann der Haupt-Thread die parallelen Gewinde vermieden werden blockiert?

+1

- "Offensichtlich, ..." - Warum? Zuerst würde ich einen Test vorschlagen, dass Sie ein Programm zur Überwachung der CPU-Auslastung öffnen und dann die Titelleiste eines Fensters halten und sich schnell bewegen, während Sie beobachten, wie sich dies auf die CPU-Nutzung auswirkt. Wenn es etwas Bedeutendes ist, dann ist deine Annahme strittig. –

+0

Remy, könnten Sie ein kleines Codebeispiel geben? – user1580348

Antwort

10

Erstens ist dieser Code nicht Thread-sicher, da der asynchrone Code von einem Task-Thread außerhalb des Haupt-UI-Threads direkt auf die TMemo zugreift. Das kannst du nicht tun. Ein Worker-Thread MUSS mit dem UI-Thread synchronisiert werden, um sicher auf UI-Steuerelemente zugreifen zu können. Andernfalls können schlimme Dinge passieren. Sie können TThread.Synchronize(), TThread.Queue() oder IOmniTask.Invoke() für diese Synchronisation verwenden.

Zweitens, während Sie die Maus auf die Titelleiste gedrückt halten, wird die Haupt-UI-Nachrichtenschleife blockiert (eine separate modale Nachrichtenschleife wird vom Betriebssystem ausgeführt, bis Sie die Maus loslassen). Daher kann der Ereignishandler OnTerminate der Task nicht ausgeführt werden, bis die Hauptnachrichtenschleife wieder die Kontrolle erlangt. Das würde erklären, warum Ihre Timer-Dauer angeblich länger ist als erwartet, nicht weil die Task-Schleife blockiert wurde.

Drittens ist Sleep() nicht absolut. Es wird für mindestens die angeforderte Menge an Zeit schlafen, kann aber länger schlafen. So wird Ihre Task-Schleife für mindestens 5 Sekunden laufen, kann aber ein wenig länger dauern.

+0

"Zugriff auf TMemo über einen Task-Thread außerhalb des Haupt-UI-Threads": Ich nehme an, dass diese Regel nur für den Worker-Thread MODIFYING eines UI-Steuerelements gilt und nicht zum LESEN eines Werts aus einem UI-Steuerelement? – user1580348

+5

@ user1580348: Nein. ** ANY ** Zugriff, Lesen oder Schreiben, muss synchronisiert werden. Und der Hauptgrund dafür ist, dass die 'TWinControl.Handle'-Eigenschaft nicht threadsicher ist, sie kann möglicherweise den Wert ändern, während ein separater Thread den' HWND' verwendet, der alle Arten von Problemen verursacht. Daher muss selbst das Lesen von Daten, die mit dem HWND verknüpft sind, geschützt werden. –

+1

Sie können auch 'PostMessage()' und eine benutzerdefinierte Nachricht senden, um die Zeichenfolge zu senden, die in der Notiz angezeigt wird. – Nat