RW-lock
RW-lock (блокировка чтения-записи, блокировка «много читателей, один писатель») – это примитив синхронизации, который позволяет решить проблему «читателей-писателя» Проблема «много читателей, один писатель» довольно часто встречается при решении практических задач многопоточного программирования. Такая проблема появляется тогда, когда много потоков читают данные, и один поток пишет (изменяет) данные. Простой подход – использованием мьютекса – считается медленным, так как нет необходимости блокировать ресурс, когда его только читают. Pthreads решает эту проблему с помощью блокировки чтения-записи, которая обеспечивает множественный доступ потоков читателей, и эксклюзивный доступ потока писателя.
В отличие от мьютекса, rwlock имеет два набора функций блокировки-освобождения ресурса – один для потоков читателей, другой для потока писателя. Инициализируется ресурс типа pthread_rwlock_t либо стандартным инициализатором PTHREAD_RWLOCK_INITIALIZER , либо функцией
int pthread_rwlock_init(pthread_rwlock_t *rwlock, const pthread_rwlockattr_t *attr);
которая принимает в качестве первого аргумента указатель на блокировку, в качестве второго – атрибуты вновь создаваемой блокировки. Во время инициализации, конечно, могут появиться ошибки, из-за которых функция «упадёт»:
- EAGAIN – у системы нет ресурсов для инициализации новой блокировки
- ENOMEM – у системы недостаток памяти для инициализации ресурса
- EPERM – вызывающая функция не обладает правами на создание блокировки
- А также ошибки, из-за которых функция может «упасть»:
- EBUSY – система обнаружила попытку повторной инициализации уже инициализированного, но не удалённого ресурса
- EINVAL – плохие атрибуты блокировки
Уничтожение блокировки производится с помощью функции
int pthread_rwlock_destroy(pthread_rwlock_t *rwlock);
которая может упасть в случае ошибок
- EBUSY – попытка уничтожения захваченной блокировки
- EINVAL – плохой аргумент функции
Блокировка осуществляется с помощью функций int
pthread_rwlock_rdlock(pthread_rwlock_t *rwlock); int pthread_rwlock_tryrdlock(pthread_rwlock_t *rwlock); int pthread_rwlock_timedrdlock(pthread_rwlock_t *restrict rwlock, const struct timespec *restrict abs_timeout);
для читателя, и
int pthread_rwlock_wrlock(pthread_rwlock_t *rwlock); int pthread_rwlock_trywrlock(pthread_rwlock_t *rwlock); int pthread_rwlock_timedwrlock(pthread_rwlock_t *restrict rwlock, const struct timespec *restrict abs_timeout);
для писателя. Эти функции и возвращаемые ошибки идентичны функциями для работы с мьютексами.
А теперь стандартный неинтересный пример
#define _CRT_SECURE_NO_WARNINGS
#define _CRT_RAND_S
#include <pthread.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#ifdef WIN32
#include <conio.h>
#include <Windows.h>
#define Sleep(X) Sleep(X)
#define wait() _getch()
#else
#define Sleep(X) sleep(x)
#define wait() scanf("1")
#endif
char* data;
typedef struct arg_tag {
pthread_rwlock_t *lock;
pthread_spinlock_t *stop_flag_lock;
int *stop;
const char* thread_name;
} arg_t;
void* reader(void *args) {
arg_t *arg = (arg_t*) args;
int stop_flag;
do {
pthread_spin_lock(arg->stop_flag_lock);
stop_flag = *arg->stop;
pthread_spin_unlock(arg->stop_flag_lock);
if (stop_flag) {
break;
}
pthread_rwlock_rdlock(arg->lock);
printf("reader %s [%s]\n", arg->thread_name, data);
pthread_rwlock_unlock(arg->lock);
Sleep(3000);
} while(1);
}
void* fwriter(void *args) {
arg_t *arg = (arg_t*) args;
int stop_flag;
unsigned int rand = 0;
do {
pthread_spin_lock(arg->stop_flag_lock);
stop_flag = *arg->stop;
pthread_spin_unlock(arg->stop_flag_lock);
if (stop_flag) {
break;
}
pthread_rwlock_wrlock(arg->lock);
free(data);
data = (char*) malloc(100);
rand_s(&rand);
rand = rand % 1000;
_itoa(rand, data, 10);
printf("\n\n\nwriter %s [%s]\n\n\n\n", arg->thread_name, data);
pthread_rwlock_unlock(arg->lock);
Sleep(rand);
} while(1);
}
int main() {
pthread_rwlock_t rw_lock;
pthread_spinlock_t stop_spin;
pthread_t readers[5];
pthread_t writer;
int stop = 0;
int i;
arg_t wtr = {
&rw_lock,
&stop_spin,
&stop,
"writer"
};
arg_t rdr[] = { {
&rw_lock,
&stop_spin,
&stop,
"reader 1"
},{
&rw_lock,
&stop_spin,
&stop,
"reader 2"
},{
&rw_lock,
&stop_spin,
&stop,
"reader 3"
},{
&rw_lock,
&stop_spin,
&stop,
"reader 4"
},{
&rw_lock,
&stop_spin,
&stop,
"reader 5"
}
};
data = (char*) malloc(100);
strcpy(data, "source");
pthread_rwlock_init(&rw_lock, NULL);
pthread_spin_init(&stop_spin, 0);
for (i = 0; i < 5; i++) {
pthread_create(&readers[i], NULL, reader, &rdr[i]);
pthread_detach(readers[i]);
}
pthread_create(&writer, NULL, fwriter, &wtr);
pthread_detach(writer);
wait();
pthread_spin_lock(&stop_spin);
stop = 1;
pthread_spin_unlock(&stop_spin);
wait();
return 0;
}
