Добавил:
Upload Опубликованный материал нарушает ваши авторские права? Сообщите нам.
Вуз: Предмет: Файл:
Сборник лекций по предмету Методы Программирова...doc
Скачиваний:
53
Добавлен:
22.09.2019
Размер:
5 Мб
Скачать
☆

Многопоточное программирование

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

Взаимодействие между потомками реализуется с помощью сообщений.

Технология MPI также используется в системах с общей памятью, но работают медленнее.

К общим данным относятся:

  • Глобальные переменные.

  • Данные, объявленные в статической памяти.

  • Данные, объявленные в динамической памяти.

Индивидуальные данные – те, которые доступны в одном потоке – локальные переменные.

Планирование потоков используется во всех современных системах. В результате потоки выполняются параллельно.

  • Если одному потоку приложения соответствует один поток ядра – одноуровневая схема.

  • Если нескольким потокам приложения соответствует одни поток ядра – многоуровневая схема.

Спецификация posix Threads

  • Разработана сообществом разработчиков программного обеспечения.

  • Предназначена для управления потоками.

IEEE's POSIX Threads Model:

• Модель программирования потоков для UNIX platform

• pthreads включает международные стандарты ISO/IEC9945-1

Программная модель pthreads определяет:

• Создание и завершение потоков

• Управление исполнением потоков

• Управление разделяемыми ресурсами процесса

Создание потока:

int pthread_ create( pthread_ t * thread, pthread_ attr_ t * attr, void*(*thread_function)(void * ), void * arg ),

  • thread –указатель на идентификатор созданного потока

  • attr – атрибуты потока

  • third argument – функция, которую поток будет исполнять

  • arg – аргументы функции (обычно структура)

  • Функция возвращает 0 при успехе.

Ожидание завершения потока:

int pthread_ join( pthread_ t thread, void * * thread_ return )

  • Основной поток дожидается завершения потока с идентификатором thread.

  • Второй аргумент – значение возвращаемое потоком

  • Функция возвращает 0 при успехе.

  • Следует всегда дожидаться завершения потока.

Пример программы с использованием posix Threads

#include <pthread.h>

#include <stdio.h>

#include <string.h>

void *potok(void *arg)

{

printf(“Работает поток%s\n”, (char*)arg);

return 0;

}

main(int argc, char *argv[])

{

int r, rc;

pthread_t threads;

if (argc>1)

{

threads=(pthread_t *)malloc((argc-1)*sizeof(pthread_t));

for(i=1; i<argc; i++)

{

rc=pthread_create(&(threads[i-1]),NULL, potok, argv[i]);

if (rc!=0) exit(-1);

}

for(i=1; i<argc; i++)

{

rc=pthread_join(threads[i-1]),NULL);

if (rc!=0) exit(-2);

}

}

}

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

Выполнение потоков по одной и той же функции может занимать разное время.

mynumber = counter;

counter++;

Рис. 17

Критические секции выполняются строго одним процессором.

Для POSIX – это любая последовательность операторов, которая должна выполняться, не прерываясь, на одном процессоре.

Взаимное иключение (mutex):

  1. Ограничивает доступ множества потоков к одному ресурсу в разделяемой памяти

  2. Обеспечивает locking/unlocking критического участка кода, где разделяемый ресурс модифицируется.

Каждый поток ждет пока mutex будет разблокирован (потоком, который его блокировал) прежде чем войти в критическую секцию.

Базовые функции:

int pthread_ mutex_ init(pthread_ mutex_ t * mutex, const pthread_ mutexattr_ t * mutexattr);

int pthread_ mutex_ lock(pthread_ mutex_ t * mutex);

int pthread_ mutex_ unlock(pthread_ mutex_ t * mutex);

int pthread_ mutex_ destroy(pthread_ mutex_ t * mutex);

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

Семафоры:

  1. Ограничивает количество потоков, исполняющих блок кода.

  2. Аналогичен mutex-ам

Основные функции:

  1. Создание: int sem_ init(sem_ t * sem, int pshared, unsigned int value);

pshared - значение 0 означает, что семафор локален для процесса

Начальное значение для семафора устанавливается в value.

  1. Уничтожение: int sem_ destroy(sem_ t * sem);

Результат действий с использованием уничтоженного семофора не определен.

Управление семафором:

  • int sem_ post(sem_ t * sem) - а увеличивает значение семафора на 1 ;

  • int sem_ wait(sem_ t * sem) - уменьшает значение семафора на 1; предварительно дождавшись пока значение не станет больше 0;

Условные переменные.

Лекция 14