Другие примитивы синхронизации Программирование с использованием POSIX thread library
Блокировки чтения/записи pthread_rwlock_init pthread_rwlock_rdlock pthread_rwlock_wrlock pthread_rwlock_unlock pthread_rwlock_destroy
Pthread_rwlock_init(3C) #include 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 int pthread_rwlock_rdlock( pthread_rwlock_t *rwlock); int pthread_rwlock_tryrdlock( pthread_rwlock_t *rwlock);
pthread_rwlock_wrlock(3C) #include 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 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 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 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
Атомарный захват нескольких мутексов void get_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); }