Многопоточность - C, C++, C# - Программирование
Навигация по сайту
Сайт:

Дополнительно:

Файловый архив:

Каталог статей:

Форум:


Категории раздела
Delphi, Pascal, ObjectPascal [18]
Программирование на Delphi, Pascal, ObjectPascal
C, C++, C# [7]
Программирование на C, C++, C#
ПХП (PHP) [6]
Все что связано с программированием на PHP.
DirectX [0]
Программирование с использованием графического API DirectX
OpenGL [0]
Программирование с использованием графического API OpenGL
Работа с базами данных (БД) [0]
Работа с базами данных MySQL и т.д. Разработка, теории, алгоритмы.
Сетевое программирование [0]
Сетевое программирование, организация сетей.
Программирование игр [0]
Все что связано с программированием игр, организацией их разработки.
Работа с мультимедиа данными [0]
Загрузка, обработка, воспроизведение и все что связано со звуком и видео.
Работа с устройсвами ввода и вывода [0]
Программирование устройств ввода и вывода. Работа с геймпадом, рулем и многим другим.
Программирование HTML 5 игр [0]
Программирование HTML 5 игр, html верстка, JS (JavaScript)
Остальное [0]
Все остальное, что не попадает ни под одну категорию.

Мини-Опрос
Какой платформой Вы пользуетесь?
Всего ответов: 914

Партнеры сайта
....

 Главная » Статьи » Программирование » C, C++, C# » Многопоточность

Многопоточность

20:58
В статье рассматриваются методы синхронизации потоков одного или нескольких процессов. Все методы основаны на создании специальных объектов синхронизации. Эти объекты характеризуются состоянием. Различают сигнальное и несигнальное состояние. В зависимости от состояния объекта синхронизации один поток может узнать об изменении состояния других потоков или общих (разделяемых) ресурсов.

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

Несинхронизированные потоки

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

#include
#include

int a[ 5 ];

void Thread( void* pParams )
{ int i, num = 0;

while ( 1 )
{
for ( i = 0; i < 5; i++ ) a[ i ] = num;
num++;
}
}

int main( void )
{
_beginthread( Thread, 0, NULL );

while( 1 )
printf("%d %d %d %d %d\n",
a[ 0 ], a[ 1 ], a[ 2 ],
a[ 3 ], a[ 4 ] );

return 0;
}
Как видно из результата работы процесса, основной поток (сама программа) и поток Thread действительно работают параллельно (красным цветом обозначено состояние, когда основной поток выводит массив во время его заполнения потоком Thread):

81751652 81751652 81751651 81751651 81751651
81751652 81751652 81751651 81751651 81751651
83348630 83348630 83348630 83348629 83348629
83348630 83348630 83348630 83348629 83348629
83348630 83348630 83348630 83348629 83348629

Запустите программу, затем нажмите "Pause" для остановки вывода на дисплей (т.е. приостанавливаются операции ввода/вывода основного потока, но поток Thread продолжает свое выполнение в фоновом режиме) и любую другую клавишу для возобновления выполнения.

Критические секции

А что делать, если основной поток должен читать данные из массива после его обработки в параллельном процессе? Одно из решений этой проблемы - использование критических секций.

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

#include
#include
#include

CRITICAL_SECTION cs;
int a[ 5 ];

void Thread( void* pParams )
{
int i, num = 0;

while ( TRUE )
{
EnterCriticalSection( &cs );
for ( i = 0; i < 5; i++ ) a[ i ] = num;
LeaveCriticalSection( &cs );
num++;
}
}

int main( void )

{
InitializeCriticalSection( &cs );
_beginthread( Thread, 0, NULL );

while( TRUE )
{
EnterCriticalSection( &cs );
printf( "%d %d %d %d %d\n",
a[ 0 ], a[ 1 ], a[ 2 ],
a[ 3 ], a[ 4 ] );
LeaveCriticalSection( &cs );
}
return 0;
}
Мьютексы (взаимоисключения)

Мьютекс (взаимоисключение, mutex) - это объект синхронизации, который устанавливается в особое сигнальное состояние, когда не занят каким-либо потоком. Только один поток владеет этим объектом в любой момент времени, отсюда и название таких объектов - одновременный доступ к общему ресурсу исключается. Например, чтобы исключить запись двух потоков в общий участок памяти в одно и то же время, каждый поток ожидает, когда освободится мьютекс, становится его владельцем и только потом пишет что-либо в этот участок памяти. После всех необходимых действий мьютекс освобождается, предоставляя другим потокам доступ к общему ресурсу.

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

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

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

#include
#include
#include

HANDLE hMutex;
int a[ 5 ];

void Thread( void* pParams )
{
int i, num = 0;

while ( TRUE )
{
WaitForSingleObject( hMutex, INFINITE );
for ( i = 0; i < 5; i++ ) a[ i ] = num;
ReleaseMutex( hMutex );
num++;
}
}

int main( void )
{
hMutex = CreateMutex( NULL, FALSE, NULL );
_beginthread( Thread, 0, NULL );

while( TRUE )

{
WaitForSingleObject( hMutex, INFINITE );
printf( "%d %d %d %d %d\n",
a[ 0 ], a[ 1 ], a[ 2 ],
a[ 3 ], a[ 4 ] );
ReleaseMutex( hMutex );
}
return 0;
}
События

А что, если мы хотим, чтобы в предыдущем примере второй поток запускался каждый раз после того, как основной поток закончит печать содержимого массива, т.е. значения двух последующих строк будут отличаться строго на 1?

Событие - это объект синхронизации, состояние которого может быть установлено в сигнальное путем вызова функций SetEvent или PulseEvent. Существует два типа событий:

Тип объекта

Описание

Событие с ручным сбросом

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

Событие с автоматическим сбросом

Объект, сигнальное состояние которого сохраняется до тех пор, пока не будет освобожден единственный поток, после чего система автоматически устанавливает несигнальное состояние события. Если нет потоков, ожидающих этого события, объект остается в сигнальном состоянии.

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

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

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

#include
#include
#include

HANDLE hEvent1, hEvent2;
int a[ 5 ];

void Thread( void* pParams )
{
int i, num = 0;

while ( TRUE )
{
WaitForSingleObject( hEvent2, INFINITE );
for ( i = 0; i < 5; i++ ) a[ i ] = num;
SetEvent( hEvent1 );
num++;
}
}

int main( void )
{
hEvent1 = CreateEvent( NULL, FALSE, TRUE, NULL );
hEvent2 = CreateEvent( NULL, FALSE, FALSE, NULL );

_beginthread( Thread, 0, NULL );

while( TRUE )
{
WaitForSingleObject( hEvent1, INFINITE );
printf( "%d %d %d %d %d\n",
a[ 0 ], a[ 1 ], a[ 2 ],
a[ 3 ], a[ 4 ] );
SetEvent( hEvent2 );
}
return 0;
}
Сравнение объектов синхронизации

В MSDN News за июль/август 1998г. есть статья об объектах синхронизации. Следующая таблица взята из этой статьи:

Объект

Относительная скорость

Доступ нескольких процессов

Подсчет числа обращений к ресурсу

Платформы

Критическая секция

быстро

Нет

Нет (эксклюзивный доступ)

9x/NT/CE

Мьютекс

медленно

Да

Нет (эксклюзивный доступ)

9x/NT/CE

Семафор

медленно

Да

Автоматически

9x/NT

Событие

медленно

Да

Да

9x/NT/CE


Категория: C, C++, C# | Просмотров: 2424 | Добавил: ДядяВолк (11.08.2010) | Рейтинг: 0.0/0
Источник: http://quadrathell.cn.ua |
HTML ссылка на материал:
BB ссылка на материал:
Похожие материалы :
Возможно вам будет интересно:
3d Rad - Как добавить свою модель (6)
Золотые Правила Игродела (0)
Физика (Blitz 3D) (0)
Как рисовать спрайты в DXDraw DelphiX (0)
30 советов по программированию в Delphi (Дельфи) часть 1 (0)
Создание наземного врага в платформере (2)
[Статья] Создаем дополнительное оружие (2)
Задротство - основа MMO игр (7)
Создаем анимацию игрока (0)
Самообучение ИИ (1)
Что с чем едят - 3d Rad (0)
Создание 2D платформера а-ля Ghost Quest (1)
Game Maker. Начало (1)
Что такое ПХП и как он работает? (0)
Создаем 2-х битный теннис на двоих без программирования (0)
Всего комментариев: 0
Добавлять комментарии могут только зарегистрированные пользователи.
[ Регистрация | Вход ]
Мы в социальных сетях

Поиск
Поиск по всему сайту:
Поиск по разделу:

Панель пользователя
Здравствуйте, Гость


Ник:
Пароль:
Запомнить :

Ваш IP: 54.158.83.210

Случайные конструкторы

Случайные движки

Случайные статьи

Статистика
Онлайн всего: 5
Гостей: 5
Пользователей: 0

На сайте были:

При полном или частичном копировании материалов сайта ссылка на Make-Games.ru обязательна. Make-Games.ru © 2008 - 2016 Хостинг от uWeb
Топ Разработка игр Рейтинг@Mail.ru