Средства синхронизации потоков, критические секции и тупики
Цель работы: практическое знакомство с методами синхронизации двух потоков одного процесса с помощью критических секций, мьютексов, семафоров и событий
В случае одновременной модификации глобальной переменной двумя и более потоками возможна потеря выполненных изменений. Для правильной работы приложений с несколькими потоками необходимо обеспечить поочередный доступ потоков к операторам программы, выполняющим изменение и запись значений переменной в память ( критическим участкам).
Только один процесс должен иметь возможность изменять и записывать значения глобальных переменных.
Для решения этой задачи могут использоваться простые средства – критические секции, обеспечивающие поочередный доступ к критическим участкам потоков одного процесса, и более сложные средства – Mutex ( mutually exclusive – взаимно- исключающий)-
в просторечии мьютексы, решающие такую же задачу для потоков, созданных различными процессами. Кроме того, задача обеспечения поочередного доступа потоков к критическим участкам может быть решена с помощью универсальных средств синхронизации - семафоров и событий.
В рассмотренном ниже приложении два потока увеличивают значение глобальной переменной Global от начального значения 100 на 1 при каждом выполнении цикла. Число повторений цикла каждого потока равно 12, результаты увеличения переменной Global выводятся на экран в виде двух списков ListBox.
При правильной работе приложения конечным значением переменной должно быть число 100+12+12=124. Однако без специальных мер такого значения получить в общем случае не удается, так как потоки " мешают ” друг другу.
Критические секции обеспечивают поочередный доступ потоков к критическим участкам программы . Для этого в начале критического участка вызывается функция
EnterCriticalSection ( имя секции) , а в конце – функция
LeaveCriticalSection ( имя секции).
Порядок выполнения работы
1. Создать приложение в соответствии с приведенным ниже текстом, в котором создаются 2 потока одного процесса, выполняющиеся с критическими секциями.
Приложение использует 1 Button, 2 ListBox , 1 CheckBox. При установке (включении) критических секций конечным результатом работы приложения должно быть значение глобальной переменной, равное 124.
2. Проверить работу мьютекса. Снять символы комментариев, поставить их на операторах использования критических секций и запустить приложение сначала с критическими секциями (вместо них теперь используется мьютекс), а затем без них.
3. Добавить возможность изменения приоритетов потоков. Запустить приложение с различными приоритетами потоков. Ответить на вопрос: как влияет изменение приоритетов потоков на результаты выполнения приложения.
unit Critsec;
interface
uses
Windows, Messages, SysUtils, Classes, Graphics, Controls, Forms, Dialogs,
StdCtrls;
type
TForm1 = class(TForm)
Button1: TButton; ListBox1: TListBox; ListBox2: TListBox;
CheckBox1: TCheckBox;
procedure FormCreate(Sender: TObject);
procedure FormDestroy(Sender: TObject);
procedure Button1Click(Sender: TObject);
end;
var
Form1 : TForm1;
crits : boolean;
sect1 : TRTLCriticalSection;
global: word;
// 2 // hmut : thandle;
implementation
{$R *.DFM}
procedure TForm1.FormCreate(Sender: TObject);
begin
initializeCriticalSection(sect1);
// 2 // hmut := createmutex(nil,true,nil);
end;
procedure TForm1.FormDestroy(Sender: TObject);
begin
DeleteCriticalSection(sect1);
//2// closehandle(hmut);
end;
function Thr1 (p:pointer):longint; stdcall;
var i,j : integer;
begin
form1.listbox1.items.clear;
for j:=1 to 12 do
begin
if crits then
EnterCriticalSection (sect1);
//2// waitforsingleobject(hmut,infinite);
sleep(10);
i:=global; i:=i+1;
form1.listbox1.items.add(intToStr(i));
global:=i;
if crits then
LeaveCriticalSection(sect1);
//2// releasemutex(hmut);
end;
end;
function Thr2 (p:pointer):longint; stdcall;
var i,j : integer;
begin
form1.listbox2.items.clear;
for j:=1 to 12 do
begin
if crits then
EnterCriticalSection (sect1);
//2// waitforsingleobject(hmut,infinite);
sleep(7);
i:=global; i:=i+1;
form1.listbox2.items.add(intToStr(i));
global:=i;
if crits then
LeaveCriticalSection(sect1);
//2// releasemutex(hmut);
end;
end;
procedure TForm1.Button1Click(Sender: TObject);
var thrid : dword;
thrh1,thrh2: thandle;
begin
global:= 100;
if CheckBox1.Checked then crits:= true
else crits:= false ;
thrh1:=CreateThread(nil,0,@Thr1,nil,0,thrid);
thrh2:=CreateThread(nil,0,@Thr2,nil,0,thrid);
end;
end.
4. Создать приложение, аналогичное заданию п.1, используя класс Tthread Delphi и проверить его работу. Сравнить полученные результаты с результатами, полученными в п.2.
5. Добавить в один из потоков разработанных в п.1 приложений операторы, завершающие выполнение одного из потоков после прохождения 5 циклов. Выполнить приложения, отследить поведение второго потока приложений.
6. Переписать разработанные приложения с использованием семафора и события и проверить правильность их работы.
7. Добавить в один из потоков разработанных приложений операторы, завершающие выполнение одного из потоков после прохождения 5 циклов. Выполнить приложения, отследить поведение второго потока приложений.
8. Используя материал лекций, разработать приложение с 2 потоками, попадающими в тупик в процессе выполнения. Параметры потоков (длительность выполнения их участков) должны задаваться при помощи ввода с клавиатуры. Проверить работу приложения при многократном запуске с различными значениями параметров.
ОТЧЕТ О РАБОТЕ
Должен быть представлен в виде текстов разработанных в приложений и письменного ответа на вопросы:
1. В чем состоит отличие критического участка от критической секции?
2. В чем состоит отличие поведения разработанных приложений, использующих для доступа потоков к критическим участкам мьютексы, критические секции, семафоры и события?
3. Как проявлялись последствия тупика потоков приложения? Всегда ли потоки при неизменных значениях параметров оказывались в тупике?
4. Использование каких средств синхронизации может приводить к тупику?
5. Распознает ли MS Windows состояние тупика потоков?
Лабораторная работа № 7