
- •Способы доступа к общим ресурсам
- •Параметры
- •Возвращаемые значения
- •Примечания
- •Параметры
- •Возвращаемые значения
- •Примечания
- •Параметры
- •Возвращаемые значения
- •Параметры
- •Возвращаемые значения
- •Примечания
- •Параметры
- •Возвращаемые значения
- •Примечания
- •Параметры
- •Возвращаемые значения
- •Примечания
- •Параметры
- •Возвращаемые значения
- •Примечания
- •Параметры
- •Возвращаемые значения
- •Примечания
- •Параметры
- •Возвращаемые значения
Министерство образования и науки РФ
Государственное бюджетное общеобразовательноеучреждение
Тверской государственный технический университет
Кафедра электронных вычислительных машин
Отчет к лабораторной работе № 4
на тему
Способы доступа к общим ресурсам
по дисциплине
“Системное программное обеспечение”
Выполнил: гр. ВМКСС 0905
Белорусова В.В.
Принял: Кучинская С.П.
Тверь
2012
Цель л/р
Понять смысл и сущность, научиться применять различные методы синхронизации доступа к общим данным в многозадачной среде.
Задание на л/р
Реализовать графическое приложение, состоящее из нескольких потоков, обращающихся к общим данным. Приложение должно применять указанный пользователем способ синхронизации доступа к общим данным.
Описание выполнения л/р
Многопоточное приложение. Операционные системы семейства Win32 являются многозадачными системами, то есть, в них одновременно могут выполняться несколько различных задач – процессов. Дополнительно каждый процесс может состоять из нескольких потоков (threads), также выполняющихся одновременно. Одновременность в данном случае подразумевается видимая пользователем; на самом же деле в единый момент времени активен только один поток одного процесса; ОС через определенный интервал времени передает управление другому потоку (возможно, другого процесса).
Каждый процесс содержит хотя бы один поток, называемый главным, или основным; по завершении этого потока завершается все приложение (включая остальные, порожденные потоки). Для создания нового потока используется функция BeginThread.
Доступ к общим ресурсам. Как уже было сказано, потоки в системе выполняются по очереди в течение определенного кванта времени, по завершении которого ОС «насильственно» передает управление другому потоку. Поэтому при доступе к общему ресурсу нескольких потоков возможно возникновение конфликтов, приводящих, например, к порче данных и, как следствие, нарушению нормального выполнения программ. Примером может послужить, например, запись данных двумя потоками в общий файл (например, файл протокола). Допустим, первый поток выполняет запись нескольких строк с некоторыми данными в файл. Однако система прерывает выполнение этого потока после записи половины строк и передает управление второму потоку. Тот, в свою очередь, записывает в файл свои данные. В итоге информация первого потока оказывается разбитой на 2 части и, возможно, непригодной для дальнейшей обработки.
Для того чтобы подобных ситуаций не возникало, и используются объекты синхронизации.
Простейшим объектом синхронизации в Win32 является критическая секция (critical section). Она действует следующим образом: в программе выделяются некоторые участки кода (критические секции), которые могут выполняться одновременно только одним потоком. Эти участки обрамляются вызовами функций EnterCriticalSection в начале и LeaveCriticalSection в конце с указанием используемого объекта критической секции (структура CRITICAL_SECTION). Поток, первым зашедший в критическую секцию, захватывает её. Соответственно, если другой поток дойдет до входа в критическую секцию, то его выполнение будет автоматически приостановлено до тех пор, пока секция не будет освобождена первым потоком. Перед использованием критическая секция должна быть проинициализирована функцией InitializeCriticalSection.
Возвращаясь к нашему примеру, если поместить в критическую секцию запись потоком в файл всех строк данных, то описанной в примере ошибки не возникнет.
Критическая секция имеет важное ограничение – она может применяться только для синхронизации потоков одного процесса. Данное ограничение можно обойти, используя мутекс (mutex). Этот объект синхронизации аналогичен критической секции, за исключением того, что он может быть использован для синхронизации потоков различных процессов. Для этого используются мутексы с именами. Для создания мутекса используется функция CreateMutex, для открытия уже существующего мутекса по имени – OpenMutex. Для обозначения места начала действия мутекса используется функция WaitForSingleObject, места окончания действия – ReleaseMutex.
Еще одним объектом синхронизации является семафор (semaphore). Семафор представляет собой мутекс со счетчиком потоков-владельцев. При создании семафора задается максимальное количество потоков, одновременно владеющих семафором. При заходе нового потока в блок семафора счетчик семафора уменьшается на единицу, соответственно, при выходе потока из семафора счетчик увеличивается на единицу. Если при попытке входа потока в семафор его счетчик равен нулю, выполнение этого потока приостанавливается. Для создания семафора используется функция CreateSemaphore, для открытия уже существующего по имени – OpenSemaphore. Для обозначения места начала действия семафора используется функция WaitForSingleObject, места окончания действия – ReleaseSemaphore.
Когда мутекс или семафор уже не нужен, нужно освободить выделенные ему системой ресурсы, вызвав для него функцию CloseHandle.
Описание приложения
Приложение должно по команде пользователя (например, по нажатию кнопки в окне) запустить n потоков (threads), где n может задаваться пользователем (но не меньше 3). При этом каждый поток должен обращаться к общему ресурсу – строке (массиву символов). Обращения эти должны иметь следующий вид – поток добавляет в конец строки 1 символ (причем каждый поток – свой уникальный символ). Если длина строки достигла заданного значения (или превысила его), содержимое строки отображается в области вывода окна приложения и строка обнуляется. Каждое такое обращение должно выполняться с синхронизацией доступа к строке. Далее поток, ничего не делая, ожидает время, необходимое операционной системе для переключения потока (достаточно ожидать 1 миллисекунду). Сформировав таким образом заданное количество строк, поток завершается.
Пользователь должен иметь возможность выбирать способ синхронизации доступа к строке. Должны быть доступны следующие способы:
Отсутствие синхронизации;
Синхронизация критической секцией или мутексом;
Синхронизация семафором.
Для случая синхронизации семафором пользователь должен иметь возможность задать максимальное значение счетчика семафора.
Результаты выполнения л/р
Рис.1. Без объекта синхронизации и без ошибок.
Рис.2. Без объекта синхронизации и с ошибками.
Рис.3. Объект синхронизации – Mutex.
Рис.4. Объект синхронизации - Semaphore
Выводы по л/р
В ходе л/р с помощью функций WinApi32 были использованы объекты синхронизации Mutex и Semaphore для безопасного пользования общими данными. По результатам л/р можно заметить, что без объекта синхронизации приложение может работать ошибочно, с объектом синхронизации никаких коллизий и ошибок не возникает.
Листинг разработанного приложения
Win32API.cs
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Runtime.InteropServices;
namespace SPO_lab_1
{
class Win32API
{
[DllImport("kernel32.dll", SetLastError = true)]
public static extern bool CloseHandle(IntPtr hObject);
//----------- Для 4 лабы --------------------//
[DllImport("kernel32.dll")]
public static extern IntPtr CreateMutex(IntPtr lpMutexAttributes, bool bInitialOwner,
string lpName);
public const int ERROR_ALREADY_EXISTS = 183;
[DllImport("kernel32.dll", SetLastError = true)]
public static extern UInt32 WaitForSingleObject(IntPtr hHandle, UInt32 dwMilliseconds);
public const UInt32 INFINITE = 0xFFFFFFFF;
public const UInt32 WAIT_ABANDONED = 0x00000080;
public const UInt32 WAIT_OBJECT_0 = 0x00000000;
public const UInt32 WAIT_TIMEOUT = 0x00000102;
[DllImport("kernel32.dll")]
public static extern bool ReleaseMutex(IntPtr hMutex);
[DllImport("kernel32.dll", SetLastError = true)]
public static extern IntPtr CreateSemaphore(ref SECURITY_ATTRIBUTES securityAttributes, int initialCount, int maximumCount, string name);
[DllImport("kernel32.dll", SetLastError = true)]
public static extern IntPtr CreateSemaphore(
IntPtr lpSemaphoreAttributes,
int lInitialCount,
int lMaximumCount,
string lpName);
[StructLayout(LayoutKind.Sequential)]
public struct SECURITY_ATTRIBUTES
{
public int nLength;
public IntPtr lpSecurityDescriptor;
public int bInheritHandle;
}
[DllImport("kernel32.dll")]
public static extern bool ReleaseSemaphore(IntPtr hSemaphore, int lReleaseCount,
IntPtr lpPreviousCount);
[DllImport("kernel32.dll")]
public static extern IntPtr OpenMutex(uint dwDesiredAccess, bool bInheritHandle,
string lpName);
public const UInt32 SYNCHRONIZE = 0x00100000;
}
}
Form1.cs
using System;
using System.Collections.Generic;
using System.ComponentModel;
using System.Data;
using System.Drawing;
using System.Linq;
using System.Text;
using System.Windows.Forms;
using System.Runtime.InteropServices;
using System.Threading;
namespace SPO_lab_1
{
public partial class Form1 : Form
{
int count_potok;
int lenght_str;
int count_str;
int count_semofor;
int select_object; // 0 - нет, 1 - мутекс, 2 - семофор
string MUTEX_NAME;
string SEMAPHORE_NAME;
String str;
List<Thread> potokList; // список процессов
IntPtr ipMutexAttr;// может ли наследоваться мутекс порожденными процессами
IntPtr ipHMutex; // мутекс
IntPtr ipSemaphoreAttr;// может ли наследоваться семафор порожденными процессами
IntPtr ipHSemaphore; // семафор
public Form1()
{
InitializeComponent();
//count_potok = lenght_str = count_str = count_semofor = select_object = 0;
//str = "";
ipMutexAttr = new IntPtr(0); // не наследуется
MUTEX_NAME = "my_Mutex";
//false - вызывающий поток создал мутекс но не является его владельцем
ipHMutex = Win32API.CreateMutex(ipMutexAttr, false, MUTEX_NAME);
SEMAPHORE_NAME = "my_Semaphore";
ipSemaphoreAttr = new IntPtr(0);
}
// при нажатии на кнопку обновляем список процесссов
private void button1_Click(object sender, EventArgs e)
{
procListBox.Items.Clear();
count_potok = Int32.Parse(textBoxN.Text);
lenght_str = Int32.Parse(textBoxL.Text);
count_str = Int32.Parse(textBoxM.Text);
count_semofor = Int32.Parse(textBoxS.Text);
select_object = comboBox1.SelectedIndex;
str = "";
for (int i = 0; i < count_potok; i++)
{
Thread my_thread = new Thread(new ThreadStart(WaitForPackets));
// должно бы создаться несколько потоков, которые будут работать параллельно. Вроде чтото работало
my_thread.Name = i.ToString();
my_thread.Start();
}
}
// Ожидание поступления пакета
public void WaitForPackets()
{
while (true)
{
// работа со строкой
switch (select_object)
{
case 0: // без синхронизации
str += Thread.CurrentThread.Name;
break;
case 1: //синхронизация по мутексу
// открываем мутекс в нашем потоке, захватываем его
ipHMutex = Win32API.OpenMutex(Win32API.SYNCHRONIZE, false, MUTEX_NAME);
if (ipHMutex != IntPtr.Zero)
{
uint fl = Win32API.WaitForSingleObject(ipHMutex, 1); // ждем пока не освободится мутекс
if (fl == Win32API.WAIT_OBJECT_0)// мутекс освободился и захвачен текущим потоком
{
str += Thread.CurrentThread.Name;
}
Win32API.ReleaseMutex(ipHMutex); //освобождаем мутекс, возвращает не 0 если всё ок.
}
break;
case 2: // синхронизация по семофору
// создаем семафор в нашем потоке, захватываем его
ipHSemaphore = Win32API.CreateSemaphore(ipSemaphoreAttr, count_potok,count_semofor, SEMAPHORE_NAME);
if (ipHSemaphore != IntPtr.Zero)
{
uint fl = Win32API.WaitForSingleObject(ipHSemaphore, 1); // ждем пока не освободится семофор
if (fl == Win32API.WAIT_OBJECT_0)// семофор освободился и захвачен текущим потоком
{
str += Thread.CurrentThread.Name;
}
Win32API.ReleaseSemaphore(ipHSemaphore,1,ipSemaphoreAttr); //освобождаем семофор, возвращает не 0 если всё ок.
}
break;
}
if (str.Length >= lenght_str) //когда достигли максимальной длины строк
{
procListBox.Invoke(new AsyncHandler(AddItem), Thread.CurrentThread, false);
}
if (procListBox.Items.Count >= count_str)
{
str = String.Format("Ending thread {0}...", Thread.CurrentThread.Name);
procListBox.Invoke(new AsyncHandler(AddItem), Thread.CurrentThread, true);
}
}
}
private void comboBox1_SelectedIndexChanged(object sender, EventArgs e)
{
if (comboBox1.SelectedIndex == 2)
textBoxS.Enabled = true;
else
textBoxS.Enabled = false;
}
private delegate void AsyncHandler(Thread thread, Boolean abort);
private void AddItem(Thread thread, Boolean abort)
{
this.procListBox.BeginUpdate();
procListBox.Items.Add(str);
str = "";
this.procListBox.EndUpdate();
if (abort)
{
thread.Abort();
}
}
}
}
Приложение «Описание функций и структур»
InitializeCriticalSection
Функция InitializeCriticalSection инициализирует объект критической секции.
VOID InitializeCriticalSection(
LPCRITICAL_SECTION lpCriticalSection
);