160 likes | 338 Views
Другие примитивы Ñинхронизации. Программирование Ñ Ð¸Ñпользованием POSIX thread library. Блокировки Ñ‡Ñ‚ÐµÐ½Ð¸Ñ / запиÑи. pthread_rwlock_init pthread_rwlock_rdlock pthread_rwlock_wrlock pthread_rwlock_unlock pthread_rwlock_destroy. Pthread_rwlock_init(3C). #include <pthread.h>
E N D
Другие примитивы синхронизации Программирование с использованием POSIX thread library
Блокировки чтения/записи • pthread_rwlock_init • pthread_rwlock_rdlock • pthread_rwlock_wrlock • pthread_rwlock_unlock • pthread_rwlock_destroy
Pthread_rwlock_init(3C) #include <pthread.h> int pthread_rwlock_init(pthread_rwlock_t *restrict rwlock, const pthread_rwlockattr_t *restrict attr); pthread_rwlock_t rwlock=PTHREAD_RWLOCK_INITIALIZER; int pthread_rwlock_destroy(pthread_rwlock_t **rwlock);
pthread_rwlock_rdlock(3C) #include <pthread.h> int pthread_rwlock_rdlock(pthread_rwlock_t *rwlock); int pthread_rwlock_tryrdlock(pthread_rwlock_t *rwlock);
pthread_rwlock_wrlock(3C) #include <pthread.h> int pthread_rwlock_wrlock(pthread_rwlock_t *rwlock); int pthread_rwlock_trywrlock(pthread_rwlock_t *rwlock);
Свойства блокировок чтения/записи • Несколько нитей могут одновременно держать одну и ту же блокировку на чтение • Блокировка на чтение рекурсивна (одна нить может захватывать ее несколько раз) • Блокировку на запись может держать только одна нить. При этом никто не может держать ту же блокировку на чтение. • Блокировка на запись нерекурсивна
Условные переменные • pthread_cond_init • pthread_cond_wait • pthread_cond_signal • pthread_cond_broadcast • pthread_cond_destroy
pthread_cond_init(3C) #include <pthread.h> int pthread_cond_init(pthread_cond_t *restrict cond, const pthread_condattr_t *restrict attr); pthread_cond_t cond= PTHREAD_COND_INITIALIZER; int pthread_cond_destroy(pthread_cond_t *cond);
pthread_cond_wait(3C) #include <pthread.h> int pthread_cond_wait(pthread_cond_t *restrict cond, pthread_mutex_t *restrict mutex); int pthread_cond_timedwait(pthread_cond_t *restrict cond,pthread_mutex_t *restrict mutex, const struct timespec *restrict abstime); /* Solaris only */ int pthread_cond_reltimedwait_np(pthread_cond_t *cond, pthread_mutex_t *mutex, const struct timespec *reltime);
Pthread_cond_signal(3C) #include <pthread.h> int pthread_cond_signal( pthread_cond_t *cond); int pthread_cond_broadcast( pthread_cond_t *cond);
Что же такое условная переменная • «Культурный» способ организации холостого цикла. • Идея в том, что условная переменная защищает какой-то предикат (условие). • Вызов pthread_cond_signal(3C)/broadcast означает, что это условие, возможно, изменилось • Это не означает, что оно действительно изменилось • Т.е. после пробуждения из wait предикат обязательно надо снова проверить. • Это касается не только timedwait, но и простого wait • Стандарт POSIX допускает ложные сработки, т.е. выход из cond_wait без вызова кем-либо signal
Еще про условную переменную • Всегда применяется в паре с мутексом. • Перед вызовом pthread_cond_wait(3C) мутекс должен быть захвачен • При входе в wait мутекс освобождается, при пробуждении снова захватывается • В соответствии с Solaris man, нельзя использовать одну и ту же переменную с разными мутексами (результат не определен) • Мутекс должен защищать данные, используемые при вычислении предиката
Применение условной переменной • Задача производитель-потребитель • Если решать только на мутексах, нужно 3 мутекса и холостой цикл на входе • Решается с одним мутексом и одной условной переменной (т.е. условная переменная ~ эквивалентна 2 мутексам)
Открытие файлов • Многопоточная программа открывает много файлов. • Например, задача «Многопоточный cp –R» • Если ручки файлов кончаются, надо подождать, пока кто-то закроет файл.
Открытие файлов - пример pthread_mutex_lock(&fileopen_mutex); do { srcfile=open(srcpathname, O_RDONLY, 0); if (srcfile < 0 && errno!=EMFILE) { perror(dstpathname); pthread_mutex_unlock(&fileopen_mutex); return NULL; } if (srcfile>0) { dstfile=open(dstpathname, O_WRONLY | O_CREAT, stat.st_mode); if (dstfile < 0 && errno != EMFILE) { perror(dstpathname); close(srcfile); pthread_cond_signal(&fileopen_cond); pthread_mutex_unlock(&fileopen_mutex); return NULL; } } if (srcfile<0 || dstfile <0) { if (srcfile>0) { close(srcfile); pthread_cond_signal(&fileopen_cond); } /* printf("waiting for file handles - opening file %s or %s\n", srcpathname, dstpathname); */ pthread_cond_wait(&fileopen_cond, &fileopen_mutex); } } while(dstfile<0 || srcfile<0); pthread_mutex_unlock(&fileopen_mutex);
Атомарный захват нескольких мутексов voidget_forks (int phil,int fork1,int fork2){ int res; pthread_mutex_lock(&getting_forks_mx); do { if (res=pthread_mutex_trylock(&forks[fork1])) { res=pthread_mutex_trylock(&forks[fork2]); if (res) pthread_mutex_unlock(&forks[fork1]); } if (res) pthread_cond_wait(&getting_forks_cond, &getting_forks_mx); } while(res); pthread_mutex_unlock(&getting_forks_mx); } void down_forks (int f1,int f2){ pthread_mutex_lock(&getting_forks_mx); pthread_mutex_unlock (&forks[f1]); pthread_mutex_unlock (&forks[f2]); pthread_cond_broadcast(&getting_forks_cond); pthread_mutex_unlock(&getting_forks_mx); }