Добавил:
Опубликованный материал нарушает ваши авторские права? Сообщите нам.
Вуз: Предмет: Файл:
Учебное пособие 300072.doc
Скачиваний:
2
Добавлен:
30.04.2022
Размер:
297.98 Кб
Скачать

Синхронизация потоков

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

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

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

Задание 1. Имеется массив целых чисел, который нужно инициализировать восходящими значениями. Сначала надо "пройти" по массиву и установить значения от 1 до 128, а затем переинициализировать этот массив значениями от 128 до 255. После этого необходимо отобразить финальный поток в окне списка.

Составить программу для данной задачи. Выполнение инициализаций реализовать в двух отдельных потоках.

Пример 1 реализации модуля с попыткой инициализировать массив в потоках:

Unit Main;

Interface

uses

Windows, Messages, SysUtils, Classes, Graphics, Controls, Forms, Dialogs, StdCtrls;

type

TMainForm = class(TForm)

Button1: TButton;

ListBoxl: 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 // Oбa потока завершены

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).

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

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