Главная » Delphi » Синхронизация потоков

0

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

Существует два типа проблем, связанных с синхронизацией потоков, и в Win32 предусмотрены различные пути их решения. В этом  разделе находятся примеры ме тодов синхронизации потоков с помощью критических секций (critical sections), мью тексов (mutexes), семафоров (semaphores) и событий.

В чем же заключается суть проблемы синхронизации потоков? Предположим, что существует  массив  целых  чисел,  который необходимо инициализировать возрастаю щими значениями. Сначала  элементы массива  инициализируются значениями от 1 до

128, а затем им присваиваются значения от 128 до 255. После  этого  результат работы

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

инициализацию в двух отдельных потоках. Попытка реализовать этот подход показа

на в коде модуля, представленного в листинге 5.4.

Листинг 5.4. Попытка инициализации одного массива в двух потоках

unit Main; interface uses

Windows, Messages, SysUtils, Classes, Graphics, Controls, Forms,

Dialogs, StdCtrls;

type

TMainForm = class(TForm)

Button1: TButton;

ListBox1: TListBox;

procedure Button1Click(Sender: TObject);

private

procedure ThreadsDone(Sender: TObject);

end;TFooThread = class(TThread)

protected

procedure Execute; override;

end;

var

MainForm: TMainForm;

implementation

{$R *.DFM}

const

MaxSize = 128;

var

NextNumber: Integer = 0;

DoneFlags: Integer = 0;

GlobalArray: array[1..MaxSize] of Integer;

function GetNextNumber: Integer;

begin

Result := NextNumber;      // возвращает глобальную переменную

Inc(NextNumber);           // инкремент глобальной переменной

end;

procedure TFooThread.Execute;

var

i: Integer;

begin

OnTerminate := MainForm.ThreadsDone;

for i := 1 to MaxSize do begin

GlobalArray[i] := GetNextNumber;        // запись элемента массива

Sleep(5);                              // смена потока

end;

end;

procedure TMainForm.ThreadsDone(Sender: TObject);

var

i: Integer;

begin

Inc(DoneFlags);

if DoneFlags = 2 then           // если оба потока завершены

for i := 1 to MaxSize do

{ заполнить список элементами массива }

Listbox1.Items.Add(IntToStr(GlobalArray[i]));

end;

procedure TMainForm.Button1Click(Sender: TObject);

begin

TFooThread.Create(False);      // создать потоки

TFooThread.Create(False);

end;

end.

Поскольку оба  потока выполняются одновременно, при  инициализации массива происходит искажение последовательности возрастания значений его элементов. Чтобы убедиться  в этом,  взгляните на результат работы данной программы, показан ной на рис. 5.4.

Рис. 5.4. Результат несинхронизированной инициализации массива

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

Источник: Тейксейра, Стив, Пачеко, Ксавье.   Borland Delphi 6. Руководство разработчика. : Пер.  с англ. — М. : Издательский дом “Вильямс”, 2002. —  1120 с. : ил. — Парал. тит. англ.

По теме:

  • Комментарии