Boost, концепция мьютекса - PullRequest
       5

Boost, концепция мьютекса

5 голосов
/ 17 февраля 2012

Я новичок в многопоточном программировании и не понимаю, как работает Mutex.В руководстве Boost :: Thread говорится:

Мьютексы гарантируют, что только один поток может заблокировать данный мьютекс.Если секция кода окружена блокировкой и разблокировкой мьютекса, гарантируется, что только секция за раз выполняет эту секцию кода.Когда этот поток разблокирует мьютекс, другие потоки могут войти в эту область кода:

Насколько я понимаю, Mutex используется для защиты части кода от одновременного выполнения несколькими потоками, НЕ защищает адрес памяти переменной.Мне сложно понять концепцию, что произойдет, если у меня есть две разные функции, пытающиеся записать на один и тот же адрес памяти.

Есть ли что-то подобное в библиотеке Boost:

  1. заблокировать адрес памяти переменной, например, double x, lock (x);Так что другие потоки с другой функцией не могут писать в x.
  2. что-то делать с x, например, x = x + rand ();
  3. unlock (x)

Спасибо.

Ответы [ 6 ]

5 голосов
/ 17 февраля 2012

Сам мьютекс гарантирует, что только один поток выполнения может заблокировать мьютекс в любой момент времени. Вы должны убедиться, что изменение связанной переменной происходит только тогда, когда мьютекс заблокирован.

C ++ дает вам способ сделать это немного легче, чем в чем-то вроде C. В C вы можете написать код правильно, гарантируя, что при любом изменении переменной вы сначала заблокируете мьютекс. (и, конечно, разблокируйте его, когда закончите).

В C ++ довольно просто инкапсулировать все это в класс с некоторой перегрузкой операторов:

class protected_int {
    int value; // this is the value we're going to share between threads
    mutex m;
public:
    operator int() { return value; } // we'll assume no lock needed to read
    protected_int &operator=(int new_value) {
        lock(m);
        value = new_value;
        unlock(m);
        return *this;
    }
};

Очевидно, что я сильно упрощаю это (до такой степени, что это, вероятно, бесполезно в нынешнем виде), но, надеюсь, вы поймете, что большая часть кода обрабатывает объект protected_int так, как если бы он был нормальная переменная.

Однако, когда вы это сделаете, мьютекс автоматически блокируется каждый раз, когда вы присваиваете ему значение, и сразу после этого разблокируется. Конечно, это в значительной степени простейший случай - во многих случаях вам нужно что-то сделать, например заблокировать мьютекс, изменить две (или более) переменные в унисон, а затем разблокировать. Однако, независимо от сложности, идея остается в том, что вы централизуете весь код, который выполняет модификацию, в одном месте, поэтому вам не нужно беспокоиться о блокировке мьютекса в остальной части кода. Если у вас есть две или более переменных вместе, как это, вам, как правило, придется блокировать мьютекс для чтения, а не только для записи - в противном случае вы можете легко получить неправильное значение, когда одна из переменных была изменена, а другая - нет. т.

3 голосов
/ 17 февраля 2012

Нет, в boost (или где-либо еще) нет ничего, что блокировало бы такую ​​память.Вы должны защитить код, который обращается к памяти, которую вы хотите защитить.

что произойдет, если у меня есть две разные функции, пытающиеся записать на один и тот же адрес памяти.

ПредполагаяВы имеете в виду 2 функции, выполняющиеся в разных потоках, обе функции должны блокировать мьютекс одинаковый , поэтому только один из потоков может записывать в переменную в данный момент времени.

Любой другой код, который обращается (либо читает, либо записывает) к той же переменной, также должен блокировать тот же мьютекс, в противном случае это приведет к неопределенному поведению.

2 голосов
/ 17 февраля 2012

Можно делать неблокирующие атомарные операции с определенными типами, используя Boost.Atomic .Эти операции не блокируют и обычно намного быстрее, чем мьютекс.Например, чтобы добавить что-то атомарно, вы можете сделать:

boost::atomic<int> n = 10;
n.fetch_add(5, boost:memory_order_acq_rel);

Этот код атомарно добавляет 5 к n.

1 голос
/ 17 февраля 2012

Я думаю, что деталь, которую вы упускаете, состоит в том, что «секция кода» - это произвольная секция кода.Это могут быть две функции, половина функции, одна строка и т.«секция кода, окруженная блокировкой и разблокировкой мьютекса», поэтому «гарантированно, что только одна секция кода одновременно выполняет эту часть кода».

Также это объясняет одно свойство мьютексов.Он не утверждает, что это единственное имущество, которое у них есть.

1 голос
/ 17 февраля 2012

Чтобы защитить адрес памяти, совместно используемый несколькими потоками в двух разных функциях, обе функции должны использовать мьютекс одинаковый ... в противном случае вы столкнетесь со сценарием, в котором потоки в любой функции могут без разбора получить доступ к той же «защищенной» области памяти.

Так что boost::mutex прекрасно работает для сценария, который вы описываете, но вы просто должны убедиться, что для данного ресурса, который вы защищаете, все пути к этому ресурсу блокируют точно такой же экземпляр объекта boost::mutex.

0 голосов
/ 17 февраля 2012

Ваше понимание верно в отношении мьютексов.Они защищают раздел кода между блокировкой и разблокировкой.

Что происходит, когда два потока записывают в одно и то же место памяти, они сериализуются.Один поток записывает свое значение, другой поток записывает в него.Проблема в том, что вы не знаете, какой поток будет писать первым (или последним), поэтому код не детерминирован.переменные.Атомарные переменные - это переменные, которые защищены компилятором или оборудованием и могут быть изменены атомарно.То есть три фазы, которые вы комментируете (чтение, изменение, запись), происходят атомарно.Взгляните на Boost atomic_count.

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...