e-mail    Debatní kniha    Mapa stránek    Hlavní  
 perličky 
 

Multithreading v Turbo Vision

Jan Navrátil

Pravděpodobně by se vám líbila možnost spustit ve vašem programu několik činností, přidělovat jim různé priority, dynamicky je přidávat a rušit. Pokud teď myslíte na Windows NT, pokusím se vás přesvědčit, že na NT nemusíte čekat. Multithreading (byť značně zjednodušený) lze provozovat i v DOSu s pomocí Turbo Vision.

Příkladem jednotlivého procesu (threadu) jsou standardní objekty HeapView nebo ClockView, které běží nezávisle na hlavním programu. Běžně se tyto objekty inicializují v TApp.Init a jejich metody Update jsou volány přímo z TApp.Idle.

Toto řešení má ale řadu nevýhod:

  • Takovéto objekty nelze za běhu programu přidávat nebo rušit
  • Nikdy nevíme, jak často je vyvolávána jejich metoda Update
  • Nelze jim přiřazovat různé priority

Můj příspěvek se snaží uvedené nevýhody odstranit.

  • Metody UpDate objektů běžících na pozadí nejsou volány přímo z TApp.Idle, ale pomocí zprávy (Event). Při kompilaci není nutné se zabývat objekty, které poběží na pozadí a není nutné doplňovat proceduru TApp.Idle!
  • Zpráva není vysílána při každém volání TApp.Idle, ale v časovém intervalu. Pro jednoduchost jsem využil činnosti přerušení 08, které se vykonává každých 55 ms. Bez ohledu na rychlost počítače je každých 55 ms vyslána zpráva zajišťující spouštění metod Update u objektů běžících na pozadí.
  • I když každý objekt na pozadí obdrží každých 55 ms zprávu, ještě to neznamená, že se bude pokaždé volat jeho metoda Update

Zde se dostávám k jádru problému. TApp.Idle vysílá takovouto zprávu: Message(App, evIdleTick, Mask, NIL). Díky evIdleTick se o ní dozví pouze ty objekty, kterým je určena (tj. běží na pozadí). Jejich metoda HandleEvent zjistí, je-li v Mask nastaven bit, který odpovídá prioritě objektu.

Každý objekt běžící na pozadí má nastavenou určitou prioritu. Od té nejvyšší, kdy je jeho metoda Update volána každých 55 ms, až po prioritu žádnou, kdy není volána vůbec. Hodnotu priority označuje jeden bit nastavený v proměnné Priority, kterou lze kdykoli změnit (pomocí SetPriority).

Jednotlivé hodnoty Mask se vysílají pomocí pole PriorityMask. Jeho jednotlivé položky mají nastaveny bity těch priorit, které mají být v daný čas provedeny. Z toho vyplývá možnost dalšího přidávání priorit (maximálně jich může být 16) a jejich nastavení podle vlastní potřeby. Každých 55 ms se vyšle pouze jedna zpráva, která může zajistit spuštění všech různých priorit, přičemž není omezen počet objektů na pozadí (stejnou prioritu může mít i několik objektů).

Při použití unitu IdleApp nezapomeňte, že:

  • Vaše objekty běžící na pozadí by měly být potomky TIdleView; není-li to možné, zkopírujte do vašeho objektu jeho metody. Nikdy nesmí být smazána (ClearEvent) zpráva evIdleTick, protože by se již nedostala k ostatním objektům, běžícím též na pozadí!
  • Procedury Update by měly být co nejkratší, aby nedocházelo ke zpomalení hlavního programu.
  • Na interval 55 ms se nelze zcela spoléhat. V případě, že nějaká část programu bude vykonávat činnost, která je časově náročná, nebude vůbec volána metoda TApp.Idle a tím se i dočasně zastaví všechny objekty na pozadí. Takovéto náročné činnosti doporučuji rozdělit do kroků a ty pak provádět na pozadí.

Použití unitu IdleApp je, doufám, patrné z přiloženého demonstračního programu. Na obrazovce budou "poletovat" různou rychlostí různé texty. Pokud hledáte praktičtější využití unitu IdleApp, nabízí se jich celá řada: tisk, hudba, budík, výpočty na pozadí.

Program byl odladěn v Borland Pascalu 7.0, ale lze ho použít i v Turbo Pascalu 6.0.

Pozn. red. -- Demonstrační program jsme pro jeho délku umístili na BBS Bajtu do knihovny souborů s názvem PERLICKY. Program najdete v souboru BBSPP10.ZIP.

{********************************************}
{*          Idle Application  v1.0          *}
{*                                          *}
{*          Jan Navrátil  (c) 1993          *}
{********************************************}

unit IdleApp;

interface

uses Objects, Views, Drivers, App;
type
  PIdleApplication = ^TIdleApplication;
  TIdleApplication = object(TApplication)
    constructor Init;
    procedure Idle; virtual;
  private
    PrevTick: LongInt;
    PriorityPos: word;
  end;

  PIdleView = ^TIdleView;
  TIdleView = object(TView)
    Priority: word;
    constructor Init(var Bounds: TRect);
    procedure HandleEvent(var Event: TEvent); virtual;
    procedure SetPriority(APriority: word);
    procedure UpDate; virtual;
  end;


const
  evIdleTick = $400;
  { prXXXX = priority }
  prNone       =  0;
  prHighest    =  1;    {  55ms }
  prHigh       =  2;    { 110ms }
  prNormal     =  4;    { 220ms }
  prLow        =  8;    { 440ms }
  prLowest     = 16;    { 880ms }

implementation

uses Dos;

const
  MaskLength = 16;
  PriorityMask : array[1..MaskLength] of word =
      (1,3,1,7,1,3,1,15,1,3,1,7,1,3,1,31);

var
  TimeTick: LongInt absolute $0000:$046C;

{******************}
{***  TIdleApp  ***}
{******************}

constructor TIdleApplication.Init;
  begin
    inherited Init;
    PrevTick := 0; PriorityPos := 0;
  end;

procedure TIdleApplication.Idle;
  begin
    inherited Idle;
    if TimeTick > PrevTick then
    begin
      PrevTick := TimeTick;
      inc(PriorityPos);
      Message(Application, evIdleTick,
              PriorityMask[PriorityPos], NIL);
      if PriorityPos = MaskLength then
                       PriorityPos := 0;
    end;
  end;

{*****************}
{**  TIdleView  **}
{*****************}
constructor TIdleView.Init(var Bounds: TRect);
begin
  inherited Init(Bounds);
  SetPriority(prNone);
end;

procedure TIdleView.HandleEvent(var Event: TEvent);
begin
  inherited HandleEvent(Event);
  if (Event.what = evIdleTick) and
     ((event.command and priority) = priority) then UpDate;
end;

procedure TIdleView.SetPriority(APriority: word);
begin
  Priority := APriority;
  if Priority = prNone then EventMask
             := EventMask and not evIdleTick
  else EventMask := EventMask or evIdleTick;
end;

procedure TIdleView.UpDate;
begin
  Abstract;
end;
end.



Pascal - hlavní
Překladače
Vlastní články
Převzaté články
Věci na stáhnutí
Odkazy k tématu
BP7 buglist
Chyba Run-time 200

BASIC