Примитивы синхронизации
Содержание
Введение
volatile
Квалификатор volatile сообщает компилятору, что выражение не нужно оптимизровать. Большинство компиляторов могут автоматически оптимизировать некоторые выражения если сочтут что значение выражения не изменяется и поэтому нет смысла перепроверять его при каждом обращении. Кроме того, компиляторы могут изменять порядок вычисления выражения при компиляции.
Использование в качестве примитва синхронизации может являться безопасным только в том случае, если запись и чтение для выражения аннотированного данным кфалификатором производится атомарно.
/*
cc -std=c11 -pthread volatile.c -o volatile
*/
#include <stdio.h>
#include <pthread.h>
#define NUM 5
volatile int is_started = 0;
void* thread_function(void* num){
int thread_num = (int) num;
while(is_started == 0);
printf("Thread %d is started\n", thread_num);
for(int i; i < 1000000; i++);
return NULL;
}
int main(){
pthread_t threads[NUM];
for(int i=0; i < NUM; i++){
pthread_create(&threads[i], NULL, thread_function, (void *) i);
}
printf("Wait to start...\n");
sleep(1);
printf("Now go!\n");
is_started = 1;
// wait to terminate
for(int i=0; i < NUM; i++){
pthread_join(threads[i], NULL);
}
return 0;
}
Спин блокировка (spin lock)
Спин-блокировка - циклическая блокировка или блокировка активного ожидания, основанная (аналогично приведенному выше примеру с volatile) на постоянном тестировании переменной блокирования, до момента ее "освобождения".
Реализуется данный вид блокировки на основе атомарных операций в качестве которых выступают асемлерные инструкции имеющиеся у процессора, как например cmpxchg "сравнение с обменом" для x86.
Основным достоинстовом и одновременно недостатком спин-блокировки является отсутствие переключения контекста выполнения, однако при этом происходит активное использование процессора что не всегда оправдано.
/*
cc -g -std=gnu11 -pthread spin_lock.c -o spin_lock
*/
#include <stdio.h>
#include <pthread.h>
#define NUM 5
pthread_spinlock_t is_started;
void* thread_function(void* num){
int thread_num = (int) num;
pthread_spin_lock(&is_started);
printf("Thread %d is started\n", thread_num);
for(int i = 0; i < 1000000; i++);
pthread_spin_unlock(&is_started);
return NULL;
}
int main(){
pthread_t threads[NUM];
pthread_spin_init(&is_started, 0);
pthread_spin_lock(&is_started);
for(int i=0; i < NUM; i++){
pthread_create(&threads[i], NULL, thread_function, (void *) i);
}
printf("Wait to start...\n");
sleep(1);
printf("Now go!\n");
pthread_spin_unlock(&is_started);
// wait to terminate
for(int i=0; i<NUM; i++){
pthread_join(threads[i], NULL);
}
pthread_spin_destroy(&is_started);
return 0;
}
Семафор (semaphore)
Семафор - объект, ограничивающий количество потоков, которые могут войти в заданный участок кода (определение введено Эдсгером Дейкстрой).
На текущий момент реализуется на основе фьютекса.
/*
cc -std=c11 -pthread semaphore.c -o semaphore
*/
#include <stdio.h>
#include <pthread.h>
#include <semaphore.h>
#define NUM 5
sem_t is_started;
void* thread_function(void* num){
int thread_num = (int) num;
sem_wait(&is_started);
printf("Thread %d is started\n", thread_num);
for(int i=0; i < 100000000; i++);
return NULL;
}
int main(){
pthread_t threads[NUM];
sem_init(&is_started, 0, 0);
for(int i=0; i < NUM; i++){
pthread_create(&threads[i], NULL, thread_function, (void *) i);
}
printf("Wait to start...\n");
sleep(1);
printf("Now go!\n");
for(int i=0; i < NUM; i++){
sem_post(&is_started);
}
// wait to terminate
for(int i=0; i < NUM; i++){
pthread_join(threads[i], NULL);
}
return 0;
}
Мьютекс (mutex)
Мьютекс (mutex, от mutual exclusion — "взаимное исключение")
/*
cc -std=c11 -pthread mutex.c -o mutex
*/
#include <stdio.h>
#include <pthread.h>
#define NUM 5
pthread_mutex_t is_started;
void* thread_function(void* num){
int thread_num = (int) num;
pthread_mutex_lock(&is_started);
printf("Thread %d is started\n", thread_num);
for(int i; i < 100000000; i++);
pthread_mutex_unlock(&is_started);
return NULL;
}
int main(){
pthread_t threads[NUM];
pthread_mutex_init(&is_started, NULL);
pthread_mutex_lock(&is_started);
for(int i=0; i<NUM; i++){
pthread_create(&threads[i], NULL, thread_function, (void *) i);
}
printf("Wait to start...\n");
sleep(1);
printf("Now go!\n");
pthread_mutex_unlock(&is_started);
// wait to terminate
for(int i=0; i<NUM; i++){
pthread_join(threads[i], NULL);
}
return 0;
}
Фьютекс (futex)
Фьютекс (futex - сокращение от англ. fast userspace mutex).
/*
cc -std=c11 -pthread futex.c -o futex
*/
#include <stdio.h>
#include <<pthread.h>
#include <linux/futex.h>
#include <syscall.h>
#define NUM 5
int is_started = 0;
int futex_wait(int *uaddr, int val) {
return syscall(SYS_futex, uaddr, FUTEX_WAIT, val, NULL, NULL, 0);
}
int futex_wake(int *uaddr, int val) {
return syscall(SYS_futex, uaddr, FUTEX_WAKE, val, NULL, NULL, 0);
}
void* thread_function(void* num){
int thread_num = (int) num;
futex_wait(&is_started, 0);
printf("Thread %d is started\n", thread_num);
for(int i; i < 100000000; i++);
return NULL;
}
int main(){
pthread_t threads[NUM];
for(int i=0; i < NUM; i++){
pthread_create(&threads[i], NULL, thread_function, (void *) i);
}
printf("Wait to start...\n");
sleep(1);
printf("Now go!\n");
futex_wake(&is_started, NUM);
// wait to terminate
for (int i=0; i<NUM; i++){
pthread_join(threads[i], NULL);
}
return 0;
}
Ссылки