методички / 4045 ЭИ
.pdfЛабораторная работа № 3
УПРАВЛЕНИЕ ПАРАЛЛЕЛЬНЫМИ ПОТОКАМИ, СВЯЗАННЫМИ СИНХРОНИЗАЦИЕЙ НА ОСНОВЕ СОБЫТИЙ
Цель работы: на основании базового примера программного кода ознакомиться с последовательностью действий объявления, выполнения и деструкции параллельных связанных потоков.
Задание
1.Загрузить архив демонстрационной программы «Многопоточность. Связанные синхронизируемые потоки. Асинхронное взаимодействие с VCL» [3.4], распаковать, запустить программу под Ubutu и ознакомиться с порядком взаимодействия потоков через событие синхронизации.
2.Создать и отладить код программы работы синхронизированных событием пото-
ков.
3.Выполнить задание организации действий в процедурах потоков по варианту преподавателя.
Ход работы
Ознакомиться с примерами-заданиями по разработке параллельных потоков с асинхронным выводом и связанными по управлению событиями. Выбрать по желанию язык программирования. На основании примеров решить задачу с достижением поставленной
вработе цели.
1.Пример-задание «Многомерные связанные потоки – описание основных классов (Lazarus)», приложение 3, П3.1. Основные данные и процедуры: класс коллекции потоков; модификация класса списка потоков, которую следует рассмотреть как пример использования полиформизма в технологиях ООП при связанном запуске и остановке потоков.
2.Пример-задание «Многомерные потоки – организация взаимодействия и синхронного управления (Lazarus)», приложение 3, П3.2. Основная форма размещения интерфейсных элементов управления списком потоков и каждым потоком в отдельности на основании событий синхронизации действий.
Результат работы
Выполняемый модуль программы с решением задачи внутри коллекции потоков. Тип задачи определяется персональным заданием преподавателя для каждого студента.
Содержание отчета
Отлаженный модуль программы с выполненным заданием
11
Контрольные вопросы
1.Концепция семафоров в управлении синхронизированными потоками.
2.Характеристики объекта события.
3.Работа объекта события как двухпозиционного ключа, основные типы сигналов событий.
БИБЛИОГРАФИЧЕСКИЙ СПИСОК
Основной
1.Молчанов А. Ю. Системное программное обеспечение: учеб. для вузов. – СПб.: Питер, 2013.
2.Таненбаум Э. Современные операционные системы / пер. с англ. – 2-е изд. – СПб:
Питер, 2003. – 1.1040 с.: ил.
3.Вильямс А. Системное программирование в Windows 2000 для профессионалов – СПб: Питер, 2003. – 624 с.: ил.
Интернет-ресурсы
1.http://www.delphisources.ru/pages/faq/base/threads_development.html
2.http://www.tutorialspoint.com/cplusplus/cpp_multithreading.htm
Загрузка примеров
1.http://rostradesamara.ru/gups/01/1/load/1459520588896/1459520609208/
threadmono.zip
2.http://rostrade-samara.ru/gups/01/1/load/1459520588896/1459520609208/ threadcollectVCL.zip
3.http://inf.samgups.org.ru/gups/01/1/load/1459520588896/1459520609208/thread.zip
4.http://rostrade-samara.ru/gups/01/1/load/1459520588896/1459520609208/ threadLinkCollec.zip
12
Приложение 1
ОДНОМЕРНЫЕ ПОТОКИ
П1.1. Одномерный поток Delphi/Lazarus
Ознакомиться с примером, создать форму. По событию кнопки создать поток и выполнить задание преподавателя по действию потока над данными (процедура DoWork).
Модуль проекта
program project1;
{$mode objfpc}{$H+}
//Добавить для программ работы с потоками
{$DEFINE UNIX} {$DEFINE UseCThreads}
//=====
uses
{$IFDEF UNIX}{$IFDEF UseCThreads} cthreads,
{$ENDIF}{$ENDIF}
Interfaces, // this includes the LCL widgetset Forms, Unit1, Unit2;
{$R *.res}
begin
RequireDerivedFormResource := True; Application.Initialize; Application.CreateForm(TForm1, Form1); Application.Run;
end.
Модуль класса потока
{Модуль объекта потока, поток выполняет действия с интервалом, указанным для sleep()}
unit Unit2;
{$mode objfpc}{$H+} interface
uses
Classes, SysUtils,Dialogs;
const CThreadPriority: array[0..6] of TThreadPriority = ( tpTimeCritical, // Приоритет реального времени tpHighest,
tpHigher,
tpNormal,
tpLower,
tpLowest,
tpIdle // процесс выполняется только тогда, когда система не занята
);
{Определение класса TMyThread} type
TMyThread = class(TThread) public
mdelay: integer; // время паузы
procedure DoWork;virtual;abstract; // процедура потока
13
Приложение 1
constructor Create(CreateSuspended: Boolean); private
{ Private declarations } protected
procedure Execute; override; end;
implementation
constructor TMyThread.Create(CreateSuspended: Boolean); begin
inherited Create(CreateSuspended);
{Если FreeOnTerminate имеет значение True, то VCL автоматически разрушает объект потока при его завершении}
FreeOnTerminate := false; // Объект будет разрушаться программно // для получения признака not Assigned()
Priority := CThreadPriority[3]; // тип приоритета - нормальный mdelay := 1000; // задержка «по умолчанию» 1 сек
end;
procedure TMyThread.Execute; begin
// Циклический вызов потока while not Terminated do begin
sleep(mdelay); Synchronize(@DoWork);
end;
end;
end.
Пример наследования потока и создание экземпляра потока
type
{ TForm1 }
// Создаем наследника потока для работы с формой
TIncDecData = class(TMyThread) public
incdata: integer; // изменяемое значение sleep() sign: boolean; // знак инкремента
procedure DoWork;override; // процедура потока constructor Create(CreateSuspended: Boolean);
private protected end;
implementation {$R *.lfm}
constructor TIncDecData.Create(CreateSuspended: Boolean); begin
inherited Create(CreateSuspended); incdata := 0;
sign := true; end;
// Основная процедура потока procedure TIncDecData.DoWork;
14
Приложение 1
begin Form1.lbInc.Caption:=intToStr(incdata);
if (sign) then // инкремент с учетом знака inc(incdata)
else dec(incdata);
end;
// Например, используя событие формы (кнопка «Старт»), создаем и запускаем поток
tread := TIncDecData.Create(true); tread.Start;
П1.2. Одномерный поток: CodeBlock C++. Настройки компилятора
Подготовка компилятора работе
1.Создать пустой проект для консольного приложения С++
2.На файловой панели проекта (Projects) правой кнопкой мыши вызвать контекстное меню и выбрать пункт «Build options...». Прописать опцию -pthread на закладках настройки компилятора и компоновщика (рис. П1.1, П1.2).
Рис. П1.1. Настройки компилятора
3. В окно основного файла проекта main.cpp вписать следующий текст:
#include <pthread.h> // модуль потока
15
Приложение 1
#include <stdio.h> #include <stdlib.h>
#include <unistd.h> // sleep(1)
void *myThreadFun(void *vargp) // Рабочая функция потока
{
sleep(1); // задержка на 1 сек. printf(«Printing hi from Thread \n»); // оператор в потоке return NULL;
} |
|
int main() |
|
{ |
|
pthread_t tid; |
// переменная типа «поток» |
printf(«Before Thread\n»); // сообщение до инициализации потока pthread_create(&tid, NULL, myThreadFun, NULL); // создать поток с рабочей функцией
pthread_join(tid, NULL); |
// включить выполнение функции myThreadFun в потоке tid |
printf(«After Thread\n»); |
// сообщение после завершения потока |
exit(0); // Завершение работы программы
}
Рис. П1.2. Настройки компановщика
16
Приложение 1
4. Построить приложение (F9) с выводом результата потока на экран
Ознакомиться с примером программы. Выполнить задание преподавателя по действию потока, изменив код функции myThreadFun.
П1.3. Одномерный поток MonoDevelop C#
Подготовка проекта к работе
1.Создать пустой проект для консольного приложения C#
2.Пример № 1. В окно основного файла проекта Program.cs поочередно вписать текст следующих примеров:
using System;
using System.Threading; // Это пространство имен поддерживает многопоточность
/* Пример одномерного потока.
Программа поочередно выводит сообщения потока и стандартного вывода с консоли, демонстрируя асинхронную независимость консоли и потока */
namespace ConsoleApplication1
{
class Program
{
// Функция запускается из потока static void func()
{
for (int i = 0; i < 10; i++) {
Console.WriteLine («Поток выводит « + i.ToString ()); Thread.Sleep (0); // Минимальная задержка в потоке
}
}
static void Main(string[] args)
{
Thread myThread = new Thread(func); //Создаем новый объект потока (Thread) myThread.Start(); // Запускаем поток
for (int i = 0; i < 10; i++ )
{
Console.WriteLine(«Консоль выводит « + i);
Thread.Sleep(0); // Минимальная задержка консольного вывода
}
Console.Read(); // Останов в ожидании нажатия клавиши myThread.Join(); // Удалить поток
}
}
}
3.Пример № 2.
using System;
using System.Threading;
// Демонстрационный пример организации одномерного потока public class Worker
{
// This method will be called when the thread is started. public void DoWork() // Процедура потока
{
17
Приложение 1
while (!_shouldStop)
{
Console.WriteLine(«worker thread: working...»);
}
Console.WriteLine(«worker thread: terminating gracefully.»);
}
public void RequestStop()
{
_shouldStop = true; // установка переменной завершения цикла
}
//Volatile is used as hint to the compiler that this data
//member will be accessed by multiple threads. private volatile bool _shouldStop;
}
public class WorkerThreadExample
{
static void Main()
{
//Create the thread object. This does not start the thread. Worker workerObject = new Worker();
//передача в поток основной процедуры
Thread workerThread = new Thread(workerObject.DoWork);
// Start the worker thread.
workerThread.Start(); // запуск процедуры в потоке Console.WriteLine(«main thread: Starting worker thread...»);
//Loop until worker thread activates. //while (!workerThread.IsAlive);
//Put the main thread to sleep for 1 millisecond to
//allow the worker thread to do some work: Thread.Sleep(1); // «Время» работы потока
//Request that the worker thread stop itself: workerObject.RequestStop();
//Use the Join method to block the current thread
//until the object's thread terminates. workerThread.Join(); // Удаление потока
Console.WriteLine(«main thread: Worker thread has terminated.»);
}
}
4. Для каждого из примеров построить приложение (Ctrl-F5) с выводом результата потока на экран
Ознакомиться с примерами программ. Примеры даны в порядке возрастания сложности. Проанализируйте текст примеров и выберите вариант для выполнения задания на базе приведенного примера. Выполнить задание преподавателя по действию потока, изменив код процедуры потока.
18
Приложение 2
МНОГОМЕРНЫЕ ПОТОКИ
П2.1. Описание основных классов в программе (Lazarus)
Программное описание класса потока и класса списка, хранящего указатели на экземпляры потока. Ознакомиться с примером, создать собственные экземпляры классов потока и списка (коллекции, цепи) хранения указателей на потоки. Вариант выполняемого задания потоком и его взаимодействие с VCL заявляется студентом, согласуется и утверждается преподавателем. Язык программирования выбирается студентом по желанию.
Главный модуль проекта
program project1; {$mode objfpc}{$H+}
//Добавить для программ работы с потоками (тестировалось для i386)
{$DEFINE UNIX} {$DEFINE UseCThreads}
//=====
uses
{$IFDEF UNIX}{$IFDEF UseCThreads} cthreads,
{$ENDIF}{$ENDIF}
Interfaces, // this includes the LCL widgetset Forms, Unit1
{ you can add units after this };
{$R *.res}
begin
RequireDerivedFormResource := True; Application.Initialize; Application.CreateForm(TForm1, Form1); Application.Run;
end.
Модуль классов потока и списка ссылок на поток. Основная работа потока - «продвигать» линию статуса до завершения большого цикла. Для вывода в главный поток используется синхронизация (Synchronize), вызываемая не на каждой итерации действия, а через значительный интервал для уменьшения побочного эффекта синхронизации вывода. Список учета потока наследуется от TthreadList с возможностью блокировки при изменении данных относительно ссылки на поток. Также добавлена процедура для полиморфного вызова деструктора потока при проходе по списку с последующей его очисткой Clear.
{Независимый процесс-поток; коллекция-лист объектов потока} unit Unit2;
{$mode objfpc}{$H+}
interface
uses
Classes, SysUtils,Dialogs,STDCTRLS,ComCtrls;
const
CThreadPriority: array[0..6] of TThreadPriority = ( tpTimeCritical, // Приоритет реального времени
19
Приложение 2
tpHighest, |
|
tpHigher, |
|
tpNormal, |
// Приоритет по умолчанию |
tpLower, |
|
tpLowest,
tpIdle // процесс выполняется только тогда, когда система не занята
);
{Класс независимого потока TMyThread} type
TMyThread = class(TThread) private
FCounter: Integer; // Переменные ProgressBar FCountTo: Integer;
PctDone: Extended; Interval: integer;
progbar: TProgressBar; // VCL - линия прогресса
mesLabel: TLabel; |
// VCL - Процент выполнения |
mRep: TMemo; |
// Отчет процессов по завершению |
StartTime,EndTime: TDateTime; |
|
procedure DoWork; |
// процедура потока связи с VCL |
public
ID: integer; // Идентификатор потока pause: boolean; // Приостановка потока function getPriority(pri: TThreadPriority): string; Procedure Report; // Отчет по заверщению Procedure starTHread; // Запуск процесса
Procedure setPriory(set_: integer); // Установка приоритета constructor Create(Suspended_: Boolean; id_:integer;
priory: integer; progbar_: TProgressBar; mesLabel_: TLabel; mem_: TMemo);
protected
procedure Execute; override; // Процедура потока end;
{Класс блокируемой коллекции потоков TCollthreadlist}
TCollthreadlist = class(tthreadlist)
procedure ClearThread; // Clear коллекции с деструкцией объектов потока
Destructor Destroy; end;
implementation
{TCollthreadlist}
Destructor TCollthreadlist.Destroy; begin
ClearThread; inherited Destroy;
end;
procedure TCollthreadlist.ClearThread; var i: integer;
begin
for i:=0 to self.LockList.Count-1 do // Проход по коллекции Tthread(LockList.Items[i]).Destroy; // Освобождение ресурса
self.LockList.Clear;
end;
20