Einzelnen Beitrag anzeigen

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