Это код реализации Sloppy Counter из OSTEP, который я сейчас читаю, и есть несколько вещей, которые я не понимаю.
Предполагается, что следующий код работает на 4-ядерном процессоре.
typedef struct __counter_t {
int global; // global count
pthread_mutex_t glock; // global lock
int local[NUMCPUS]; // local count (per cpu)
pthread_mutex_t llock[NUMCPUS]; // ... and locks
int threshold; // update frequency
}counter_t;
// init: record threshold, init locks, init values
// of all local counts and global count
void init(counter_t* c, int threshold) {
c->threshold = threshold;
c->global = 0;
pthread_mutex_init(&c->glock, NULL);
int i;
for (i = 0; i < NUMCPUS; i++) {
c->local[i] = 0;
pthread_mutex_init(&c->llock[i], NULL);
}
// update: usually, just grab local lock and update local amount
// once local count has risen by ‘threshold’, grab global
// lock and transfer local values to it
void update(counter_t* c, int threadID, int amt) {
pthread_mutex_lock(&c->llock[threadID]);
c->local[threadID] += amt; // assumes amt > 0
if (c->local[threadID] >= c->threshold) { // transfer to global
pthread_mutex_lock(&c->glock);
c->global += c->local[threadID];
pthread_mutex_unlock(&c->glock);
c->local[threadID] = 0;
}
pthread_mutex_unlock(&c->llock[threadID]);
}
// get: just return global amount (which may not be perfect)
int get(counter_t* c) {
pthread_mutex_lock(&c->glock);
int val = c->global;
pthread_mutex_unlock(&c->glock);
return val; // only approximate!
}
Почему для каждого локального счетчика в __counter_t должна быть блокировка? В функции update() идентификатор потока передается в качестве аргумента, поэтому не означает ли это, что только один поток сможет получить доступ к local[threadID]? И если произойдет переключение контекста, другой поток получит доступ только к local[threadID], который соответствует их идентификатору потока. Я не понимаю, почему потоки должны быть заблокированы перед доступом к своим собственным local[NUMCPUS], поскольку каждый элемент внутри массива не будет доступен другим потокам, кроме их собственного, и ни один другой поток не вызовет update() с тем же идентификатором потока.





Почему должна быть блокировка для каждого локального счетчика
Цитировать книгу
Кроме этих счетчиков есть еще блокировки: по одной на каждый локальный счетчик1, и одна на глобальный счетчик.
Что означает 1? В конце страницы:
1 Нам нужны локальные блокировки, потому что мы предполагаем, что на каждом ядре может быть более одного потока. Если бы вместо этого на каждом ядре работал только один поток, локальная блокировка не понадобилась бы.
Спасибо, не заметил мелкие надписи внизу страницы
Обратите внимание, что у идентификатора
__counter_tесть две проблемы. Это зарезервированный идентификатор в соответствии со стандартом C , потому что он начинается с двух символов подчеркивания, и это зарезервированный идентификатор в системах POSIX, потому что он заканчивается на_t.