Шаг 300.
Модели потоков. Потокозащищенные классы Delphi

    На этом шаге мы перечислим классы, которые могут использоваться в потоках и приведем общие рекомендации по созданию и использованию потоков.

    Многие классы Delphi имеют методы, которые позволяют синхронизировать доступ к переменным классов при обращении к ним из различных потоков.

    Класс TCanvas - имеет методы Lock и Unlock. После вызова метода Lock рисование на полотне (canvas) возможно только из потока, откуда осуществлен вызов этой команды, а остальные потоки находятся в состоянии ожидания. Пример, который обсуждался на 285 шаге - вывод графики на экран при одновременной сортировке случайного массива в разных потоках, - можно переписать с использованием этих методов:

procedure TSortThread.DoVisualSwap;
begin	
  with FBox do	
  try
    Canvas.Lock;	
    Canvas.Pen.Color	:= clBtnFace;
    PaintLine(Canvas, FI, FA);
    PaintLine(Canvas, FJ, FB);
    Canvas.Pen.Color := clRed;
    PaintLine(Canvas, FI, FB);
    PaintLine(Canvas, FJ, FA);
  finally	
    Canvas.Unlock;	
  end;	
end;	

    Переписанный код можно вызывать без использования метода Synchronize, причем он будет работать быстрее.

    Графические объекты - потомки TGraphicsObject, которые инкапсулируют функциональность графических объектов Windows - кисть (TBrush), перо (ТРеn) и шрифт (TFont), также имеют методы Lock и Unlock. Использование и назначение их такое же, как и в случае класса TCanvas.

    Методами Lock и Unlock, которые используются для защиты переменных при доступе к ним из разных потоков, также обладают следующие классы Delphi:

    Класс TThreadList наиболее часто используется при создании многопоточных приложений. Он хранит массив указателей, причем берет у операционной системы столько ресурсов, сколько надо для хранения массива данной размерности. Путем приведения типов в нем можно хранить и целочисленные переменные (integer), и переменные с плавающей точкой (single). Перекрыв деструктор класса, в классе-потомке можно хранить список объектов. Эти разнообразные возможности и делают данный класс часто применимым. Типичный пример работы с классом TThreadList:

var
  UserList:TThreadList = nil;

procedure TTest.AfterConstruction;
var
  L:TList;
begin
  inherited;
  try
    L := UserList.LockList;
    L.Add(Self);
  finally
    UserList.UnlockList;
  end;
end;

initialization
  TComponentFactory.Create(ComServer, TTest,
    Class_Test, ciMultiInstance, tmFree);
  UserList := TThreadList.Create;
finalization
  UserList.Free;
end.

    В глобальной переменной UserList сохраняются ссылки на клиентов, которые обращаются к серверу за данными. Вызов метода LockList возвращает ссылку на класс TList, с которым манипулируют как с обычным списком. После вызова метода UnlockList использовать полученную ссылку на список запрещается. Каждому вызову метода Lock должен соответствовать вызов метода Unlock. Поэтому во всех описанных выше классах методы блокировки доступа (Lock, LockList) и снятия блокировки (Unlock, UnlockList) следует помещать в защищенный блок try...finally...end. Несоблюдение этого правила приведет к тому, что к данному экземпляру класса нельзя будет обратиться из других потоков, кроме потока, вызвавшего метод Lock. Поскольку все описанные выше блокировки реализованы через критические секции, то поток, который вызвал метод Lock, может повторно его вызвать и выполнить код после этого вызова, даже если метод Unlock после первого вызова выполнен не был. Это затрудняет поиск ошибок, которые возникают при невыполненном методе Unlock.

    Выводы.

    Многозадачная и многопоточная среда Win32 предоставляет широкие возможности для написания высокоэффективных приложений. Однако написание приложений, использующих многопоточность и взаимодействующих друг с другом, при неаккуратном программировании может привести к их неверной работе, неоправданной загрузке и даже блокировке всей системы. Во избежание этого следуйте нижеприведенным рекомендациям.

    Закончив изучение потоков, мы можем перейти к детальному рассмотрению внутрипроцессных СОМ-серверов - именно при их создании важно правильно организовать работу с потоками. Этому вопросу будут посвящены следующие шаги.

    Со следующего шага мы начнем рассматривать создание внутрипроцессорных серверов автоматизации.




Предыдущий шаг Содержание Следующий шаг