Sloppy Counter и Multiple Locks в одной программе

Это код реализации 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() с тем же идентификатором потока.

Обратите внимание, что у идентификатора __counter_t есть две проблемы. Это зарезервированный идентификатор в соответствии со стандартом C , потому что он начинается с двух символов подчеркивания, и это зарезервированный идентификатор в системах POSIX, потому что он заканчивается на _t.

Andrew Henle 30.10.2022 04:22
Стоит ли изучать PHP в 2026-2027 годах?
Стоит ли изучать PHP в 2026-2027 годах?
Привет всем, сегодня я хочу высказать свои соображения по поводу вопроса, который я уже много раз получал в своем сообществе: "Стоит ли изучать PHP в...
Поведение ключевого слова "this" в стрелочной функции в сравнении с нормальной функцией
Поведение ключевого слова "this" в стрелочной функции в сравнении с нормальной функцией
В JavaScript одним из самых запутанных понятий является поведение ключевого слова "this" в стрелочной и обычной функциях.
Приемы CSS-макетирования - floats и Flexbox
Приемы CSS-макетирования - floats и Flexbox
Здравствуйте, друзья-студенты! Готовы совершенствовать свои навыки веб-дизайна? Сегодня в нашем путешествии мы рассмотрим приемы CSS-верстки - в...
Тестирование функциональных ngrx-эффектов в Angular 16 с помощью Jest
В системе управления состояниями ngrx, совместимой с Angular 16, появились функциональные эффекты. Это здорово и делает код определенно легче для...
Концепция локализации и ее применение в приложениях React ⚡️
Концепция локализации и ее применение в приложениях React ⚡️
Локализация - это процесс адаптации приложения к различным языкам и культурным требованиям. Это позволяет пользователям получить опыт, соответствующий...
Пользовательский скаляр GraphQL
Пользовательский скаляр GraphQL
Листовые узлы системы типов GraphQL называются скалярами. Достигнув скалярного типа, невозможно спуститься дальше по иерархии типов. Скалярный тип...
1
1
67
1
Перейти к ответу Данный вопрос помечен как решенный

Ответы 1

Ответ принят как подходящий

Почему должна быть блокировка для каждого локального счетчика

Цитировать книгу

Кроме этих счетчиков есть еще блокировки: по одной на каждый локальный счетчик1, и одна на глобальный счетчик.

Что означает 1? В конце страницы:

1 Нам нужны локальные блокировки, потому что мы предполагаем, что на каждом ядре может быть более одного потока. Если бы вместо этого на каждом ядре работал только один поток, локальная блокировка не понадобилась бы.

Спасибо, не заметил мелкие надписи внизу страницы

Mattmmmmm 30.10.2022 09:01

Другие вопросы по теме