AGB  ·  Datenschutz  ·  Impressum  







Anmelden
Nützliche Links
Registrieren
Thema durchsuchen
Ansicht
Themen-Optionen

Liste per Thread abarbeiten

Ein Thema von norwegen60 · begonnen am 2. Feb 2014 · letzter Beitrag vom 3. Feb 2014
Antwort Antwort
norwegen60

Registriert seit: 23. Dez 2007
Ort: Schwarzwald
504 Beiträge
 
Delphi 12 Athens
 
#1

Liste per Thread abarbeiten

  Alt 2. Feb 2014, 18:09
Hallo,

ich bin Anfänger im programmieren von Threads und das lesen von den vielen Themen erzeugt macht einem schnell klar, dass man hier viel falsch machen kann. Deshalb frage ich hier lieber mal nach.

Die Aufgabe:
Ich habe ein Thread das im Execute-Teil kontinuierlich eine Digitalkarte ausliest.
Delphi-Quellcode:
procedure TthrIO.execute;
begin
  while not Terminated do
  begin
    Sleep(50);
    _MeasureDigIn;
  end;
end;

procedure TthrIO._MeasureDigIn;
{*******************************************************************************
Liest die Anzahl definierter Digitaleingänge ein und stellt Istwerte als
    Longint-Werte blockweise zur Verfügung
      _iDigIn[0] = FirstportXX,
      _iDigIn[1] = SecondportXX, ...
*******************************************************************************}

var
  iPort, // Port A, B, CL, CH = 1, 2, 3, 4, ...
  iULStat : Integer;
  iValue : TiWerteArr;


begin
  for iPort := 1 to iMaxPort do
  begin
    iULStat := cbDIn (iAdr, GetPortType(iPort), iValue[iBlock]);
    if iULStat > 0 then inc(iFailedDIn);
  end;

  _iDigIn := iValue;
end;

function TthrIO.GetDigIn(iKan:Integer):Boolean;
{*******************************************************************************
Abruf der Einzelwerte von extern
Der Gesamtwert für Kanal  0..23 liegt in _iDigIn[0]
                        24..47 in _iDigIn[1], usw
*******************************************************************************}

var
  iPort : Integer;
  iVal : Longint;
begin
  Result := false;
  if succ(iKan)>iDigIn then exit;

  iPort := trunc(iKan/24);
  iVal := _iDigIn[iPort];
  Result:=odd(iVal shr iKan);
end;
Parallel soll es aber auch möglich sein, Digitalausgänge zu setzen. Ich hatte hier verschiedene Ansätze

1. Der direkte Zugriff auf einzelne Kanäle am Thread vorbei
Delphi-Quellcode:
procedure TthrIO.SetDigOut(iKan:Integer; bValue:Boolean);
{*******************************************************************************
Setzt je nach Kanal den entsprechenden Port-Wert, der dann im Thread kontinu-
ierlich ans Geraet gesendet wird. Dabei wird b erücksichtigt, dass u.U. schon
Ports für Digitaleingänge reserviert sind
*******************************************************************************}

var
  iULStat : Integer;

begin
  iULStat := cbDBitOut (iAdr, FIRSTPORTA, iKan, Integer(bValue));
  if iULStat > 0 then inc(iFailedDOut);
end;
Wohl nicht die saubere Art bei Threads

2. Karte bietet auch Möglichkeit alle DigOuts mit einem Integer zu setzen. Diesen dann wieder in Execute beschreiben
Delphi-Quellcode:
TthrIO.execute;
begin
  while not Terminated do
  begin
    Sleep(50);
    _SetDigOut;
    _MeasureDigIn;
  end;
end;

procedure TthrIO._SetDigOut;
{*******************************************************************************
Setzt die Digitalausgänge blockweise über Excute
      _iDigOutWerte[0] = FirstportXX,
      _iDigOutWerte[1] = SecondportXX, ...
*******************************************************************************}

var
  iPort, // Port A, B, CL, CH = 1, 2, 3, 4, ...
  iPortType,
  iULStat : Integer;

begin
  for iPort := 1 to iMaxPortDigOut do
  begin
    // Nur Senden, wenn sich Werte geändert haben
   if _iDigOutWerte[Pred(iPort)] <> iDigOutWerte[Pred(iPort)] then
    begin
      iPortType := GetPortType(iMaxPortDigIn + iPort);
      iULStat := cbDOut (iAdr, iPortType, iDigOutWerte[iPort-1]);
      if iULStat <> 0 then
         inc(iFailedDOut)
      else
        _iDigOutWerte[Pred(iPort)] := iDigOutWerte[Pred(iPort)];
    end;
  end;
end;

procedure TthrIO.SetDigOut(iKan:Integer; bValue:Boolean);
{*******************************************************************************
Setzt je nach Kanal den entsprechenden Port-Wert, der dann im Thread kontinu-
ierlich ans Geraet gesendet wird. Dabei wird b erücksichtigt, dass u.U. schon
Ports für Digitaleingänge reserviert sind
*******************************************************************************}

var
  iPort,
  iPortType,
  iULStat,
  iKorr : Integer;

begin
  iKan := iKan + iDigIn;
  iPort := pred(GetPort(iKan));
  iKorr := iKan - GetShl(iPort+1) - (iKan div 24) * 24;

  iPort := iPort - iMaxPortDigIn;
  if bValue then
     iDigOutWerte[iPort] := iDigOutWerte[iPort] or (1 shl iKorr)
   else
     iDigOutWerte[iPort] := iDigOutWerte[iPort] and (not (1 shl iKorr));
end
Nachteil dieser Methode: Ausgänge werden nicht in der Reihenfolge gesetzt, wie es das Programm macht. Im Gegenteil: Da Karte mit 8bit Ports arbeitet, werden Kanäle im 1. Port immer vor denen im 2. gesetzt. Eben auch dann, wenn ich z.B. zuerst Ausgang 9 und dann 4 gesetzt habe.

3. Möglichkeit:
Ich hänge neue Werte in einer Liste immer hinten an und der Thread arbeitet diese wieder von oben nach unten ab und lösche erfolgreich gesetzte Ausgänge in der Liste wieder.
Delphi-Quellcode:
  TValDigOut = record
    Kan : integer;
    Value : Boolean;
  end;

  TArrValDigOut = Array of TValDigOut;

procedure TthrIO._SetDigOut;
{*******************************************************************************
Setzt jeden Digitalausgang der Reihe nach wie sie gesetzt wurden
*******************************************************************************}

var
  iULStat : Integer;

  procedure DeleteArrayIndex(var X: TArrValDigOut; Index: Integer);
  begin
    Finalize(X[Index]);
    System.Move(X[succ(Index)], X[Index], (high(X) - Index) * succ(SizeOf(TValDigOut)));
    SetLength(X, high(X));
  end;

begin
  while high(DigOutWerte) > 0 do
  begin
    iULStat := cbDBitOut (iAdr, FIRSTPORTA, DigOutWerte[0].Kan, Integer(DigOutWerte[0].Value));
    if iULStat <> 0 then
      inc(iFailedDOut)
    else
      DeleteArrayIndex(DigOutWerte, 0);
  end;
end;

procedure TthrIO.SetDigOut(iKan:Integer; bValue:Boolean);
{*******************************************************************************
Schreibt den zu setzenden Kanal in eine Liste die dann _SetDigOut abarbeitet
*******************************************************************************}

begin
  SetLength(DigOutWerte, succ(length(DigOutWerte)));
  with DigOutWerte[high(DigOutWerte)] do
  begin
    Kan := iKan;
    Value := bValue;
  end;
end;
Hier beginnt aber meine Unsicherheit, ob das was ich hier mach auch Threadsicher ist bzw. was ich machen muss, damit es es wird.

Viele Grüße
Gerd
  Mit Zitat antworten Zitat
Furtbichler
(Gast)

n/a Beiträge
 
#2

AW: Liste per Thread abarbeiten

  Alt 2. Feb 2014, 18:27
Wenn die Karte das unabhängige Lesen und Schreiben erlaubt, dann spricht nichts gegen Möglichkeit #1.

Die beiden anderen Möglichkeiten sind aber auch interessant, aber nur dann einzusetzen, wenn es sinnvoll ist. Das wäre dann der Fall, wenn Du viele Consumer der 'SetDigOut' hast, die sich auf die Füße treten könnten. Dann wäre eben #2 oder #2 sinnvoller. Welche von den Möglichkeiten Du verwenden willst, bleibt Dir überlassen. Beide haben Vor- und Nachteile die Du aber selbst einschätzen musst, denn ich kenne die Eigenschaften der Karte nicht.
  Mit Zitat antworten Zitat
OlafSt

Registriert seit: 2. Mär 2007
Ort: Hamburg
284 Beiträge
 
Delphi 10.2 Tokyo Professional
 
#3

AW: Liste per Thread abarbeiten

  Alt 3. Feb 2014, 11:16
Wenn es wichtig ist, in welcher Reihenfolge die Ausgänge gesetzt werden, dann ist ein Blick auf die TQueue-Klasse einen Blick wert. In diese Queue "schiebt" man sozusagen seine Schaltanweisung hinein und der Thread nimmt sie in derselben Reihenfolge auch wieder heraus (FIFO-Prinzip). Synchronisieren kann man das mit CriticalSections oder Events, je nach Geschmack.

Spielt die Reihenfolge keine Rolle, dann Option #2.
  Mit Zitat antworten Zitat
norwegen60

Registriert seit: 23. Dez 2007
Ort: Schwarzwald
504 Beiträge
 
Delphi 12 Athens
 
#4

AW: Liste per Thread abarbeiten

  Alt 3. Feb 2014, 17:50
Hallo zusammen,

Ich habe heute gemerkt, dass die Probleme die ich hatte, zwei verschiedene Ursachen hatten:
  1. Ich habe in Lösung 1 nicht überprüft, ob der Kanal auch im zulässigen Bereich liegt und falls einer außerhalb angesprochen wurde hat sich das Programm aufgehängt. Hatte also nichts mit Thread zu tun
  2. Zusätzlich wurde die Karte beim Einschalten eines Motors hin und wieder gestört. Hier hat nach Rücksprache mit dem Hersteller eine besser Masseverbindung geholfen
Ich habe mich deshalb für Lösung 1 entschieden. Wobei ich an die Karte immer blockweise sende, so dass wenn ein Eingang aus irgendwelchen Gründen zurückgesetzt wurde, dies wieder korrigiert wird. Dadurch, dass SetDigOut kanalweise aufgerufen wird, stimmt auch die Reihenfolge.

Danke für euer Feedback

Gerd
  Mit Zitat antworten Zitat
Antwort Antwort


Forumregeln

Es ist dir nicht erlaubt, neue Themen zu verfassen.
Es ist dir nicht erlaubt, auf Beiträge zu antworten.
Es ist dir nicht erlaubt, Anhänge hochzuladen.
Es ist dir nicht erlaubt, deine Beiträge zu bearbeiten.

BB-Code ist an.
Smileys sind an.
[IMG] Code ist an.
HTML-Code ist aus.
Trackbacks are an
Pingbacks are an
Refbacks are aus

Gehe zu:

Impressum · AGB · Datenschutz · Nach oben
Alle Zeitangaben in WEZ +1. Es ist jetzt 06:15 Uhr.
Powered by vBulletin® Copyright ©2000 - 2024, Jelsoft Enterprises Ltd.
LinkBacks Enabled by vBSEO © 2011, Crawlability, Inc.
Delphi-PRAXiS (c) 2002 - 2023 by Daniel R. Wolf, 2024 by Thomas Breitkreuz