Добавил:
Upload Опубликованный материал нарушает ваши авторские права? Сообщите нам.
Вуз: Предмет: Файл:

Zemskov_my_sppo1

.pdf
Скачиваний:
30
Добавлен:
18.04.2015
Размер:
595.32 Кб
Скачать

Ю. В. Земсков. Системное и прикладное программное обеспечение. Конспект лекций, варианты заданий и методические указания в лабораторным работам. ВГИ ВолГУ, 2002 г.

Листинг 4.3. Пример многопоточной программы на Си

//MS Visual C++ 5. Создание тредов.

//Каждый тред подсчитывает кол-во пробелов в своём файле.

//Главная программа запускается с параметрами - списком имён файлов

//(при отладке в среде - Project|Settings|Debug|Program arguments)

//Используются только функции Win32 API.

#include<windows.h>

#include<stdio.h>

#include<conio.h>

#ifdef _MT //Перед компиляцией надо установить опцию:

//Project | Settings | C/C++ | Code Generation | Use run-time:= Multithrieded

//К проекту надо добавить библиотеку LIBCMT.LIB (для правильного printf) #include<process.h> // подключается вместо стандартной библиотеки -

// для многопоточных приложений это обязательно

#endif

//Эта функция будет запускаться в отдельных тредах,

//поэтому тип возвращаемого значения именно такой: static unsigned int __stdcall mythread(LPVOID lpFileName){

//будем считать кол-во пробелов в файле

HANDLE handle;

// Дескриптор файла

DWORD numRead, total;

 

 

char buf;

// Сюда будем читать очередной символ из файла

printf("\nThis is thread %lu for file %s",

GetCurrentThreadId(), lpFileName);

handle=CreateFile(

//

Открываем файл

(LPCTSTR)lpFileName,

//

имя файла

GENERIC_READ,

 

//

для чтения

FILE_SHARE_READ,

// др.процессы тоже смогут читать этот файл

NULL,

// режимы

безопасности поддерживаются только в NT и 2000

OPEN_EXISTING,

// файл должен существовать, иначе ошибка

FILE_ATTRIBUTE_NORMAL,

// атрибуты файла - без атрибутов

NULL);

 

 

// дескриптор файла для расширенных атрибутов

total=0;

 

 

 

 

do{ // В цикле читаем очередной символ из файла

ReadFile(handle,(

// Дескриптор файла

 

LPVOID)&buf,

// Адрес буфера, куда читаем

 

1,

 

// Сколько байт читать

 

&numRead,

// Фактически прочитанное кол-во байт

 

NULL);

 

// Структура перекрытия не используется

if(buf==0x20) total++;

// 0x20 - это код пробела

} while (numRead>0);

// пока ещё что-то читается

printf("\n File %s --- %d

spaces", lpFileName,total);

// Следующие три строки -

если надо вывести сообщения в отдельных формах с кнопкой OK:

char szMsg[80];

 

 

 

wsprintf( szMsg, "%d spaces in file %s\n", total, lpFileName); MessageBox( NULL, szMsg, "Thread", MB_OK );

CloseHandle(handle);

// закрыли файл

return 0;

 

}

 

int main(int argc,char*argv[]){ int i;

unsigned int threadid;

HANDLE hThrd[255]; // массив ссылок на треды

for(i=0;i<(argc-1);i++){ // Создаём тред

hThrd[i]=(HANDLE)_beginthreadex( //WinAPI-функция CreateThread не рекомендуется

NULL,

// Атрибуты

безопасности поддеживаются только

в NT и Win2000

0x4000,

// Размер стека в

байтах

 

 

//(если 0 -

то тред использует стек родителя)

 

mythread, // указатель на

функцию, к-рая будет выполняться в потоке

(LPVOID)argv[i+1],

//

указатель на параметр потока (в нашем случае имя файла)

0,

 

//

поток

сразу выполняется

 

 

 

//

(если

CREATE_SUSPEND - то ждёт вызова ResumeThread)

&threadid);

 

//

выходной параметр : идентификатор

потока

printf("\n===MAIN : thread %u with ID=%lu started for file %s ===",i,threadid,argv[i+1]);

Sleep(300); // пауза 300 мс

}

printf("\nPRESS ANY KEY...");

getch(); // ждать нажатия на любую клавишу return 0;

}

33

Ю. В. Земсков. Системное и прикладное программное обеспечение. Конспект лекций, варианты заданий и методические указания в лабораторным работам. ВГИ ВолГУ, 2002 г.

Листинг 4.4. Реализация метода CPaint

procedure TForm1.CPaint( Cv: TCanvas ); var

b: byte; с: Tcolor; r: TRect;

begin

Cv.Brush.Style := bsSolid;

с:= random(256); b := random(256):

с:= с or (b shl 8); b := random(256):

с:= с or (b shl 16); Cv.Brush.Color := c; r.left := random(90); r.top := random(90); r.right := r.left+10; r.bottom := r.top+10; Cv.Rectangle(r); end;

Листинг 4.5. Реализация методов Paint1 и Paint2

procedure Tform1.Paint1; begin

CPaint( PaintBox1.Canvas ); end;

procedure TForml.Paint2; begin

CPaint( PaintBox2.Canvas ); end;

Листинг 4.6. Реализация метода Execute

procedure TMyThread. Execute; begin

while not Terminated do

if Box1 then Synchronize(Form1.Paint1) else Synchronize(Form1.Paint2);

end;

Листинг 4.7. Реализация метода Button1Click

procedure TForm1.Button1Click(Sender: TObject); begin

T1 := TMyThread.Create(true); T1.Box1 := true;

T1.Priority := tpLower;

T2 := TMyThread.Create(true); T2.Boxl := false;

T2.Priority := tpLowest; T1.Resume;

T2.Resume;

end;

34

Ю. В. Земсков. Системное и прикладное программное обеспечение. Конспект лекций, варианты заданий и методические указания в лабораторным работам. ВГИ ВолГУ, 2002 г.

tpLowest — приоритет на два пункта ниже нормального; tpLower — приоритет на один пункта ниже нормального; tpNormal — нормальный приоритет;

tpHigher — приоритет на один пункт выше нормального; tpHighest — приоритет на два пункта выше нормального; tpTimeCritical — максимальный приоритет.

В раздел interface модуля Unit1 нужно добавить ссылку на модуль Unit2, где описан класс TMyThread, а в раздел implementation модуля Unit2 — ссылку на модуль Unit1. Программу можно откомпилировать и запустить. После щелчка на кнопке начнётся заполнение двух областей рисования небольшими прямоугольниками, причём, в правой области это будет происходить заметно медленнее.

4.9. Методы синхронизации потоков

То сказано глупцом и признано глупцами, Что будет смерть для нас творить ужасный свет! Пока на свете мы, она ещё не с нами; Когда ж пришла она, то нас на свете нет!1

Жуковский

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

Влистинге 4.8 приведён пример двухпотокового приложения в Delphi. Каждый поток заполняет массив GlobalArray числами, беря значение очередного элемента из глобальной переменной GlobalNumber, каждый раз увеличивая её значение на заданную величину step. Т. к. действия потоков не синхронизированы, то в результате элементы массива GlobalArray будут иметь случайные значения.

Влистинге 4.9 для синхронизации потоков используется критическая секция. Для этого описана переменная CS типа TRTLCriticalSection. Перед запуском потоков критическая секция инициализируется (InitializeCriticalSection). Для обозначения начала критической секции используется вызов EnterCriticalSection, для обозначения конца — LeaveCriticalSection. После использования необходимо освободить системные ресурсы, отведённые для реализации критической секции (вызов

DeleteCriticalSection).

Если один из потоков вошёл в критическую секцию, то любой другой, дойдя до начала одноимённой секции, будет ожидать, пока первый не выйдет из неё. В результате сначала один из потоков заполнит массив числами от 0 до 199, а затем второй — числами от 200 до 299. После завершения второго потока

вкомпонент ListBox будут выведены значения элементов глобального массива (от 200 до 299).

Влистинге 4.10 для синхронизации потоков используется мьютекс. Для этого описана переменная hMutex типа THandler. Перед запуском потоков мьютекс инициализируется (CreateMutex). Перед обращением к разделяемому ресурсу вызывается функция WaitForSingleObject, которая проверяет состояние мьютекса. Первый поток увидит, что мьютекс свободен (находится в сигнальном состоянии) и начнёт работу с глобальными переменными. Второму потоку придётся ожидать освобождения мьютекса, которое произойдёт в результате выполнения вызова ReleaseMutex. После использования необходимо освободить системные ресурсы, отведённые для реализации мьютекса (вызов CloseHandle).

Влистинге 4.11 для синхронизации потоков используется семафор. От мьютекса он отличается

тем, что при его использовании программист может задавать число потоков, которые могут получать одновременный доступ к разделяемому ресурсу (в нашем примере это число равно 1). Как и ранее, описана переменная hSem типа THandler. Перед запуском потоков семафор инициализируется (CreateSemaphore). Перед обращением к разделяемому ресурсу вызывается, как и в предыдущем примере, функция WaitForSingleObject, которая проверяет состояние мьютекса и, если счётчик мьютекса не равен нулю, то уменьшает его значение на 1, а иначе ожидает освобождения ресурса в течение указанного времени (в нашем примере время не ограничивается). Вызов ReleaseSemaphore увеличивает значение счётчика семафора на 1. После использования необходимо освободить системные ресурсы, отведённые для реализации семафора (вызов CloseHandle).

1Типичный пример рассинхронизации двух потоков.

35

Ю. В. Земсков. Системное и прикладное программное обеспечение. Конспект лекций, варианты заданий и методические указания в лабораторным работам. ВГИ ВолГУ, 2002 г.

Листинг 4.8. Пример выполнения несинхронизированных потоков в Delphi

// Два потока заполняют общий массив - пример для Delphi unit Unit1;

interface uses

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

type

TForm1 = class(TForm) // Класс формы Button1: TButton;

ListBox1: TListBox;

procedure Button1Click(Sender: TObject); private

// Эта процедура будет выводить результат в компонент ListBox procedure ThreadsDone(Sender: TObject);

end;

TMyThread = class (TThread) // Класс потока protected

procedure Execute; override; end;

var Form1: TForm1;

implementation {$R *.dfm}

const m = 100; var

GlobalNumber : Integer = 0;

DoneFlags : Integer = 0; GlobalArray : array[1..m] of Integer;

procedure TForm1.Button1Click(Sender: TObject); begin

TMyThread.Create(False); // Создали первый поток - сразу активный TMyThread.Create(False); // Создали второй поток - сразу активный

end;

procedure TForm1.ThreadsDone(Sender: TObject); var i : Integer;

begin

Inc(DoneFlags); // Счётчик завершённых потоков

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

for i := 1 to m do // распечатать значения элементов массива ListBox1.Items.Add(IntToStr(GlobalArray[i]));

end;

//Функция GetNextNumber возвращает целое число -

//текущее значение глобальной переменной GlobalNumber,

//затем значение глобальной переменной увеличивается на step function GetNextNumber(step : Integer): Integer;

begin

Result := GlobalNumber; Inc(GlobalNumber,step); end;

procedure TMyThread.Execute; var i : Integer;

begin

// процедура ThreadsDone будет выполняться после завершения потока: OnTerminate := Form1.ThreadsDone;

for i := 1 to m do begin GlobalArray[i] := GetNextNumber(1);

Sleep(2); // Этот поток будет приостановлен на 2 мс end;

end;

end.

36

Ю. В. Земсков. Системное и прикладное программное обеспечение. Конспект лекций, варианты заданий и методические указания в лабораторным работам. ВГИ ВолГУ, 2002 г.

Листинг 4.9. Пример использования критической секции в Delphi

unit Unit1; interface uses

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

type

TForm1 = class(TForm) Button1: TButton; ListBox1: TListBox;

procedure Button1Click(Sender: TObject); private

procedure ThreadsDone(Sender: TObject); end;

TMyThread = class (TThread) // Класс потока protected

procedure Execute; override; end;

var Form1: TForm1;

implementation {$R *.dfm} const m = 100; var

GlobalNumber : Integer = 0;

DoneFlags : Integer = 0; GlobalArray : array[1..m] of Integer; CS : TRTLCriticalSection;

procedure TForm1.Button1Click(Sender: TObject); begin

InitializeCriticalSection(CS); // Создали критическую секцию TMyThread.Create(False); // Создали первый поток - сразу активный TMyThread.Create(False); // Создали второй поток - сразу активный

end;

procedure TForm1.ThreadsDone(Sender: TObject); var i : Integer;

begin Inc(DoneFlags);

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

for i := 1 to m do // Распечатать значения элементов глобального массива ListBox1.Items.Add(IntToStr(GlobalArray[i]));

DeleteCriticalSection(CS); // Удалили критическую секцию end;

end;

//Функция возвращает целое число -

//текущее значение глобальной переменной GlobalNumber,

//затем значение глобальной переменной увеличивается на step function GetNextNumber(step : Integer): Integer;

begin

Result := GlobalNumber;

Inc(GlobalNumber,step);

end;

 

procedure TMyThread.Execute; var i : Integer;

begin

// процедура ThreadsDone будет выполняться после завершения потока: OnTerminate := Form1.ThreadsDone;

EnterCriticalSection(CS); // Начало критической секции for i := 1 to m do begin

GlobalArray[i] := GetNextNumber(1); Sleep(2); end;

LeaveCriticalSection(CS); // Конец критической секции end;

end.

37

Ю. В. Земсков. Системное и прикладное программное обеспечение. Конспект лекций, варианты заданий и методические указания в лабораторным работам. ВГИ ВолГУ, 2002 г.

Листинг 4.10. Пример использования мьютексов в Delphi

unit Unit1; interface uses

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

type

TForm1 = class(TForm) Button1: TButton; ListBox1: TListBox;

procedure Button1Click(Sender: TObject); private

procedure ThreadsDone(Sender: TObject); end;

TMyThread = class (TThread) // Класс потока protected

procedure Execute; override; end;

var Form1: TForm1;

implementation {$R *.dfm} const m = 100; var

GlobalNumber : Integer = 0;

DoneFlags : Integer = 0; GlobalArray : array[1..m] of Integer; hMutex : THandle = 0;

procedure TForm1.Button1Click(Sender: TObject);

begin

//

Создадим мьютекс - без атрибутов безопасности,

 

//

без владельца (доступный),

без имени:

hMutex :=

CreateMutex(nil, False, nil);

TMyThread.Create(False);

// Создали

первый поток - сразу активный

TMyThread.Create(False);

// Создали

второй поток - сразу активный

end;

 

 

 

 

procedure TForm1.ThreadsDone(Sender: TObject); var i : Integer;

begin Inc(DoneFlags);

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

for i := 1 to m do // Распечатать значения элементов глобального массива ListBox1.Items.Add(IntToStr(GlobalArray[i]));

CloseHandle(hMutex); // Уничтожили мьютекс end;

end;

function GetNextNumber(step : Integer): Integer;

begin

 

Result := GlobalNumber;

Inc(GlobalNumber,step);

end;

 

procedure TMyThread.Execute; var i : Integer;

begin

FreeOnTerminate := True; // Тред будет уничтожаться после выполнения автоматически OnTerminate := Form1.ThreadsDone;

// Если мьютекс доступен, то вызов WaitForSingleObject возвращает WAIT_OBJECT_0: if WaitForSingleObject(hMutex, INFINITE) = WAIT_OBJECT_0 then begin

for i := 1 to m do begin

GlobalArray[i] := GetNextNumber(1); Sleep(2); end;

end;

ReleaseMutex(hMutex);

end;

end.

38

Ю. В. Земсков. Системное и прикладное программное обеспечение. Конспект лекций, варианты заданий и методические указания в лабораторным работам. ВГИ ВолГУ, 2002 г.

Листинг 4.11. Пример использования семафоров в Delphi

unit Unit1; interface

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

type

TForm1 = class(TForm) Button1: TButton; ListBox1: TListBox;

procedure Button1Click(Sender: TObject); private

procedure ThreadsDone(Sender: TObject); end;

TMyThread = class (TThread) // Класс потока protected

procedure Execute; override; end;

var Form1: TForm1;

implementation {$R *.dfm} const m = 100; var

GlobalNumber : Integer = 0;

DoneFlags : Integer = 0; GlobalArray : array[1..m] of Integer; hSem : THandle = 0;

procedure TForm1.Button1Click(Sender: TObject); begin

//Создадим семафор, без атрибутов, нач.и макс.значение счётчика=1, без имени: hSem := CreateSemaphore(nil, 1, 1, nil);

TMyThread.Create(False); // Создали первый поток - сразу активный TMyThread.Create(False); // Создали второй поток - сразу активный

end;

procedure TForm1.ThreadsDone(Sender: TObject); var i : Integer;

begin Inc(DoneFlags);

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

for i := 1 to m do // Распечатать значения элементов глобального массива ListBox1.Items.Add(IntToStr(GlobalArray[i]));

CloseHandle(hSem);

end;

end;

function GetNextNumber(step : Integer): Integer; begin

Result := GlobalNumber; Inc(GlobalNumber,step); end;

procedure TMyThread.Execute;

var i : Integer; WaitReturn : DWORD; begin

OnTerminate := Form1.ThreadsDone;

WaitReturn := WaitForSingleObject(hSem, INFINITE); if WaitReturn = WAIT_OBJECT_0 then begin

for i := 1 to m do begin

GlobalArray[i] := GetNextNumber(1); Sleep(2); end;

end;

// Увеличим счётчик семафора на 1, без сохранения его прежнего знчения: ReleaseSemaphore(hSem, 1, nil);

end;

end.

39

Ю. В. Земсков. Системное и прикладное программное обеспечение. Конспект лекций, варианты заданий и методические указания в лабораторным работам. ВГИ ВолГУ, 2002 г.

4.10. Варианты заданий

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

Рене Декарт

1. Модифицировать программу (см. листинг 4.1, 4.2 или 4.3) так, чтобы каждый поток осуществлял вывод на консоль, только если работа других потоков с консолью уже закончена или ещё не начата. Для синхронизации использовать:

а) критические секции; б) мьютексы; в) семафоры.

2.Модифицировать программу (см. листинг 4.1, 4.2 или 4.3) так, чтобы каждому потоку передавался

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

а) проверку всех элементов массива в цикле;

б) ожидание с помощью вызова WaitForMultipleObjects;

3. Задача “читатели–писатели”. Два класса процессов имеют доступ к некоторому разделяемому ресурсу (области памяти, файлу, порту устройства ввода–вывода). “Читатели” — это процессы, которые могут параллельно считывать информацию из ресурса, а “писатели” — процессы, записывающие информацию в этот ресурс. Провести моделирование данной ситуации с выводом результатов в наглядном виде на экран. Варианты:

а) приоритет выше у “читателей”: если хотя бы один “читающий” процесс получил доступ к ресурсу, то он закрывается для “писателей”;

б) приоритет выше у “писателей”: если хотя бы один “пишущий” процесс запросил доступ к ресурсу, то он закрывается для “читателей”, запросивших доступ к этому ресурсу после этого;

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

4. Задача о пяти обедающих философах (классическая задача параллельного программирования, предложена Эдсгером Дейкстрой). В давние времена один богатый филантроп пожертвовал свой капитал на учреждение некоего пансиона, чтобы дать пристанище пяти знаменитым философам. У каждого философа была своя комната, где он мог предаваться размышлениям. Была у них и общая столовая с круглым столом, вокруг которого стояло пять стульев, каждый помеченный именем того философа, которому он предназначался. Звали философов Phil1, Phil2, Phil3, Phil4 и Phil5, и за столом они располагались в этом же порядке против часовой стрелки. Слева от каждого философа лежала золотая вилка, а в центре стола стояла большая миска спагетти, содержимое которой постоянно пополнялось. Предполагалось, что большую часть времени философ проводил в размышлениях, но, почувствовав голод, шёл в столовую, садился на свой стул, брал слева от себя вилку и приступал к еде. Но такова уж сложная природа спагетти, что их не донести до рта без помощи второй вилки. Поэтому философу приходилось брать вилку и справа от себя. Закончив трапезу, он клал на место обе вилки, выходил из-за стола и возвращался к своим размышлениям. Разумеется, одной вилкой философы могли пользоваться только по очереди. Если же вилка требовалась другому философу, ему приходилось ждать, пока она освободится. Таким образом, жизнь каждого философа представляет собой повторение цикла из семи последовательно сменяющих друг друга состояний: 1) размышляет; 2) хочет кушать; 3) садится; 4) берёт левую вилку (если она занята, то ждёт её освобождения); 5) берёт правую вилку (если она занята, то ждёт её освобождения); 6) ест; 7) кладёт обе вилки.

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

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

40

Ю. В. Земсков. Системное и прикладное программное обеспечение. Конспект лекций, варианты заданий и методические указания в лабораторным работам. ВГИ ВолГУ, 2002 г.

в) Другое решение задачи — появление слуги (решение предложено Карелом С. Шолтеном). В обязанности слуги входит сопровождение каждого философа за стол и из-за стола. Слуге даётся секретное указание следить за тем, чтобы за столом никогда не оказывалось более четырёх философов одновременно. Поскольку вилок в этой ситуации всегда больше, то опасности дедлока нет. Хотя появляется другая опасность. Предположим, что левая рука у сидящего философа весьма медлительна, в то время как его левый сосед в высшей степени проворен. Прежде чем философ дотянется до своей левой вилки, его левый сосед быстро вступает в дело, садится, хватает обе вилки и долго ест. Рано или поздно наевшись, он кладёт обе вилки на стол и покидает своё место. Но стоит ему встать, как он снова ощущает голод, возвращается к столу, садится, мгновенно хватает обе вилки — и всё это прежде, чем его медлительный, долгосидящий

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

ипроворен, как было описано, то неизбежно кто-нибудь (или он, или его сосед) очень долгое время будет оставаться голодным. В такой ситуации не видно эффективного решения к достижению общего удовлетворения, кроме как приобрести достаточное количество вилок (по условию задачи проблем с поставками спагетти не имеется). Однако, чтобы все-таки гарантировать доступ к спагетти для каждого сидящего философа, нужно изменить поведение слуги: проводив философа до его места, слуга должен ждать, пока философ не возьмёт обе вилки и только после этого может позволить сесть его соседям. Однако, остаётся более философская проблема. Предположим, что слуга испытывает иррациональную неприязнь к одному из философов и постоянно затягивает исполнение своей обязанности проводить его к столу, даже если философ к этому готов. Это ситуация, которую мы не можем описать формально, поскольку она неотличима от той, когда философу требуется бесконечно длительное время, чтобы проголодаться. Заметим, что слуга может быть реализован в виде семафора, максимальное значение счётчика которого должно быть на 1 меньше числа философов.

г) Условия те же, что и в пункте 4а, но правила упрощены: философ может брать вилки в произвольном порядке.

д) Условия те же, что и в пункте 4б, но правила упрощены: философ может брать вилки в произвольном порядке.

е) Условия те же, что и в пункте 4в, но правила упрощены: философ может брать вилки в произвольном порядке.

5. Броуновское движение. Несколько частиц (каждая — от заданной начальной точки) совершают хаотичное движение, двигаясь в случайном направлении на случайное расстояние (в пределах экрана). Получить на экране изображение (и при желании пользователя — траекторию движения) частиц до прерывания с клавиатуры. Удары частиц друг о друга (при пересечении траекторий) и о стенки экрана считать абсолютно упругими. Каждую частицу изображать своим цветом. Каждая частица реализуется в отдельном потоке.

41

Ю.В. Земсков. Системное и прикладное программное обеспечение. Конспект лекций, варианты заданий и методические указания в лабораторным работам. ВГИ ВолГУ, 2002 г.

5.Программирование сокетов

5.1. Использование классических блокирующих сокетов

Порядок использования блокирующих сокетов с установлением соединения:

На стороне сервера:

1.Инициализировать DLL-библиотеку Winsock (WSAStartup) (только для платформы Windows).

2.Создать сокет (socket).

3.Связать сокет (bind).

4.Перевести его в слушающий режим (listen).

5.Принять запрос на соединение (accept).

6.Получить (recv) или отправить (send) данные.

7.Отключить сокет (shutdown).

8.Закрыть сокет (close) (для Windows — closesocket).

9.Закрыть библиотеку Winsock (WSACleanup) (только для платформы Windows).

На стороне клиента:

1. Инициализировать DLL-библиотеку Winsock (WSAStartup) (только для платформы Windows) 2. Создать сокет (socket).

3.Послать запрос на соединение (connect).

4.Отправить (send) или получить (recv) данные.

5.Отключить сокет (shutdown).

6.Закрыть сокет (close) (для Windows — closesocket).

7.Закрыть библиотеку Winsock (WSACleanup) (только для платформы Windows).

Порядок использования блокирующих сокетов без установления соединения (одинаково для сервера и клиента):

1.Инициализировать DLL-библиотеку Winsock (WSAStartup) (только для платформы Windows).

2.Создать сокет (socket).

3.Связать сокет (bind).

4.Получить (recvfrom) или отправить (sendto) данные.

5.Закрыть сокет (close) (для Windows — closesocket).

6.Закрыть библиотеку Winsock (WSACleanup) (только для платформы Windows).

Рассмотрим пример сервера и клиента Winsock (листинги 5.1 и 5.2), написанные на Си (консольные приложения). Здесь клиент соединяется с сервером и посылает ему сообщение, состоящее из пяти символов. Сервер принимает сообщение и выводит его на экран.

Сервер можно отлаживать с помощью Telnet (Windowsntelnet.exe), который будет играть роль пока не написанного клиента.

Обе программы компилируются как в Microsoft Visual C, так и в C++ Builder’е.

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

42

Соседние файлы в предмете [НЕСОРТИРОВАННОЕ]