- •Гоувпо «Воронежский государственный технический университет»
- •Методические указания
- •Требования к выполнению и оформлению лабораторных работ
- •Практическое применение потоков
- •Основы работы объекта tThread
- •Завершение работы потока
- •Преимущества однопоточного интерфейса пользователя
- •Метод Synchronize
- •Хранение локальных данных потоков
- •Использование объекта tThread для хранения данных
- •Threadvar: хранение локальных данных потоков с помощью интерфейса api
- •Синхронизация потоков
- •Критические разделы
- •Мьютексы
- •Семафоры
- •Библиографический список
- •Содержание
- •394026 Воронеж, Московский просп., 14
Синхронизация потоков
При работе с несколькими потоками вам часто придется синхронизировать доступ потоков к определенным данным или ресурсам. Предположим, у вас есть приложение, в котором один поток используется для чтения файла в память, а другой для подсчета количества символов в файле. Само собой разумеется, что до тех пор, пока файл полностью не загрузится в память, вы не сможете сосчитать в нем все символы. Но поскольку каждая операция выполняется в своем собственном потоке, операционная система вольна обращаться с ними как с двумя совершенно несвязанными задачами. Чтобы справиться с этой проблемой, необходимо синхронизировать эти два потока таким образом, чтобы поток, подсчитывающий символы, не начинал работать, пока не завершится поток, загружающий файл.
Существует два типа проблем, связанных с синхронизацией потоков. В 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. Результат несинхронизированной инициализации массива
Решение этой проблемы лежит в синхронизации двух потоков во время их доступа к глобальному массиву, чтобы они не исчезали из виду в одно и то же время. Но к реализации решения этой проблемы можно подойти по-разному.