Мутексы

Добро пожаловать в замечательный мир мутексов ( mutex = mutual exclusion, что переводится как «взаимное исключение» ). Из названия видно, что мутексы предназначены для того, чтобы сделать определнный код или данные доступными только одному потоку одновременно.

Использовать мутексы очень просто. После того, как мутекс создан, вы просто вставляете команду pthread_mutex_lock() перед кодом, использующим защищенные данные и pthread_mutex_unlock() сразу после этого кода. В этом случае нам надо защищать a и b как во время чтения, так и во время модификации.

Для начала напишем код создания и удаления мутекса. Поместим мутекс вот в такую структуру:

typedef struct
{
int a;
int b;
int result;
int result2;
int use_count;
int use_count2;
int max_use;
int max_use2;
pthread_mutex_t mutex;
} app_data;

Создавать и уничтожать мутекс будем в функции main(). Будем передавать NULL в качестве параметра attr, что означает использование параметров мутекса по умолчанию:

main()
{
pthread_t ct,ut,st;
app_data td={5,5,0,0,0,0,100,0};
void *retval; pthread_mutex_init(&td.mutex;,NULL);
pthread_create(&ut;,NULL,user_thread,&td;);
pthread_create(&ct;,NULL,changer_thread,&td;); pthread_join(ct,&retval;);
pthread_join(ut,&retval;);
pthread_mutex_destroy(&td.mutex;);
printf(«result should be %d, is %d\n»,td.max_use*(5+5),td.result);
}
Теперь добавим вызовы устанавливающие и снимающие блокировки перед кодом, использующим защищенные переменные( чтение и запись ). Поменяем цикл while() в user_thread:

while(usesmax_use)
{
pthread_mutex_lock(&td-;>mutex);
if (td->a==5)
{
td->result+=(td->a+td->b);
td->use_count++;
uses++;
}
pthread_mutex_unlock(&td-;>mutex);
usleep(1);
}
И цикл, изменяющий эти переменные:

while ((td->use_count+td->use_count2)<(td->max_use+td->max_use2))
{
pthread_mutex_lock(&td-;>mutex);
if (td->a==5)
{
td->a=50;
td->b=td->a+usleep(1000);
}
else
{
td->a=5;
td->b=td->a+usleep(1000);
}
pthread_mutex_unlock(&td-;>mutex);
usleep(1);
}
На этот раз после компиляции и запуска вы получите:

result should be 1000, but is 1000
Это то, что нам нужно.

А что если у вас только один поток изменяет данные, но больше чем один их читают ?

Добавим еще один поток, использующий a и b:

void *subtracter_thread(void *data)
{
int use=0;
app_data *td=(app_data*)data;
while(usemax_use2)
{
pthread_mutex_lock(&td-;>mutex);
if (td->a==50)
{
td->result2-=(td->a+td->b);
use++;
td->use_count2++;
}
pthread_mutex_unlock(&td-;>mutex);
usleep(1);
} return 0;
}
Добавим запуск этого потока в функцию main() и установим значение переменной max_use2 в структуре app_data:

main()
{
pthread_t ct,ut,st;
app_data td={5,5,0,0,0,0,100,100};
void *retval; pthread_mutex_init(&td.mutex;,NULL);
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_mutex_destroy(&td.mutex;);
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);
}
Скомпилируем и запустим:

result should be 1000, is 1000
result2 should be -10000, is -10000

Стоп. А не должны ли user_thread и subtracter_thread читать значение a и b одновременно ? ( Помните, что код, заключенный между вызовами блокировки и разблокировки мутекса не может быть запущен, пока этот мутекс блокирован в другом потоке. ) Ни одна из них не именяет a и b, так что мы вполне можем читать их одновременно, пока они не изменяются в changer_thread.