Rwlocks

С помощью блокирующего механизма, rwlocks, сделать это несложно. В отличие от мутексов, rwlock может иметь два состояния блокировки: чтение и запись. Пока rwlock не заблокирован на запись, любой поток может устанавливать и снимать блокировку на чтение. Если же установлена блокировка на запись, то все блокировки на чтение блокируются, пока блокировка на запись не снята.

Для начала поменяем мутекс на rwlock в нашей структуре данных:

typedef struct
{
int a;
int b;
int result;
int result2;
int use_count;
int use_count2;
int max_use;
int max_use2;
pthread_rwlock_t rwl;
} app_data;
Дальше, поменяем инициализацию мутекса на rwlock в функции main (). Опять же используем NULL в качестве атрибутов rwlock:

main()
{
pthread_t ct,ut,st;
app_data td={5,5,0,0,0,0,100,100};
void *retval;

pthread_rwlock_init(&td.rwl;);
pthread_create(&ut;,NULL,user_thread,&td;);
pthread_create(&ct;,NULL,changer_thread,&td;);
pthread_create(&st;,NULL,subtracter_thread,&td;);
pthread_join(st,&retval;);
pthread_join(ct,&retval;);
pthread_join(ut,&retval;);
pthread_rwlock_destroy(&td.rwl;);
printf(«result should be %d, is %d\n»,td.max_use*(5+5),td.result);
printf(«result2 should be %d, is %d\n»,-(td.max_use2*(50+50)),td.result2);
}

Теперь поменяем pthread_mutex_lock на pthread_rwlock_rdlock и pthread_mutex_unlock на pthread_rwlock_unlock в user_thread и changer_thread. Это обезопасит доступ на чтение. Также заменим pthread_mutex_lock на pthread_rwlock_wrlock и pthread_mutex_unlock на pthread_rwlock_unlock в функции changer_thread function, что обезопасит доступ на запись:

void *user_thread(void *data)
{
int uses=0;
app_data *td=(app_data*)data;
while(usesmax_use)
{
pthread_rwlock_rdlock(&td-;>rwl);
if (td->a==5)
{
td->result+=(td->a+td->b);
td->use_count++;
uses++;
}
pthread_rwlock_unlock(&td-;>rwl);
usleep(1);
} return 0;
} void *changer_thread(void *data)
{
app_data *td=(app_data*)data;
while ((td->use_count+td->use_count2)<(td->max_use+td->max_use2))
{
pthread_rwlock_wrlock(&td-;>rwl);
if (td->a==5)
{
td->a=50;
td->b=td->a+usleep(1000);
}
else
{
td->a=5;
td->b=td->a+usleep(1000);
}
pthread_rwlock_unlock(&td-;>rwl);
usleep(1);
} return 0;
} void *subtracter_thread(void *data)
{
int use=0;
app_data *td=(app_data*)data;
while(usemax_use2)
{
pthread_rwlock_rdlock(&td-;>rwl);
if (td->a==50)
{
td->result2-=(td->a+td->b);
use++;
td->use_count2++;
}
pthread_rwlock_unlock(&td-;>rwl);
usleep(1);
} return 0;
}
Вот оно! Теперь subtracter_thread и user_thread могут читать данные одновременно, а changer_thread не имеет к ним доступ, пока они используются в subtracter_thread или в user_thread.

И последнее, что стоит сказать о rwlocks: они не могут накладываться друг на друга. То есть вы не можете дважды заблокировать rwlock, предварительно его не разблокировав. Также rwlock нельзя заблокировать на чтение и на запись одновременно. Все это было сделано для того, чтобы максимально облегчить системную реализацию rwlocks.