Атомные операции в C ++ - PullRequest
6 голосов
/ 18 марта 2012

У меня есть набор функций C ++:

funcB(){};
funcC(){};
funcA()
{
    funcB();
    funcC();
}

Теперь я хочу сделать funcA атомарными, то есть funcB и funcC вызовы внутри funcA должны выполняться атомарно.Есть ли способ добиться этого?

Ответы [ 3 ]

11 голосов
/ 18 марта 2012

Один из способов добиться этого - использовать новые (C ++ 11) функции std::mutex и std::lock_guard.

Для каждого защищенного ресурса вы создаете один глобальный std::mutex;каждый поток затем блокирует этот мьютекс, как требуется, путем создания std::lock_guard:

#include <thread>
#include <iostream>
#include <mutex>
#include <vector>

// A single mutex, shared by all threads. It is initialized
// into the "unlocked" state
std::mutex m;

void funcB() {
  std::cout << "Hello ";
}
void funcC() {
  std::cout << "World." << std::endl;
}
void funcA(int i) {

  // The creation of lock_guard locks the mutex
  // for the lifetime of the lock_guard
  std::lock_guard<std::mutex> l(m);

  // Now only a single thread can run this code
  std::cout << i << ": ";
  funcB();
  funcC();

  // As we exit this scope, the lock_guard is destroyed, 
  // the mutex is unlocked, and another thread is allowed to run
}

int main () {
  std::vector<std::thread> vt;

  // Create and launch a bunch of threads
  for(int i =0; i < 10; i++)
    vt.push_back(std::thread(funcA, i));

  // Wait for all of them to complete
  for(auto& t : vt)
    t.join();
}

Примечания:

  • В вашем примере некоторый код, не связанный с funcAможет вызвать либо funcB, либо funcC без учета блокировки, установленной funcA.
  • В зависимости от структуры вашей программы, вы можете по-разному управлять временем жизни мьютекса.Например, он может быть членом класса, который включает funcA.
6 голосов
/ 18 марта 2012

В общем, НЕТ. Атомные операции очень точно определены. То, что вы хотите, это семафор или мьютекс.

1 голос
/ 18 марта 2012

Если вы используете GCC 4.7, тогда вы можете использовать новую функцию Транзакционная память для выполнения следующих действий:

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

В настоящее время транзакции поддерживаются только в C ++ и C в форме операторов транзакций, выражений транзакций и функциональных транзакций. В следующем примере будут прочитаны и a, и b, а разница будет записана в c, все атомарно и изолированно от других транзакций:

__transaction_atomic { c = a - b; }

Следовательно, другой поток может использовать следующий код для одновременного обновления b, даже не заставляя c содержать отрицательное значение (и не используя другие конструкции синхронизации, такие как блокировки или атомарные элементы C ++ 11):

__transaction_atomic { if (a > b) b++; }

Точная семантика транзакций определяется в терминах модели памяти C ++ 11 / C1X (см. Ниже ссылку на спецификацию). Грубо говоря, транзакции предоставляют гарантии синхронизации, аналогичные тем, которые были бы гарантированы при использовании единой глобальной блокировки в качестве защиты для всех транзакций. Обратите внимание, что, как и другие конструкции синхронизации в C / C ++, транзакции основаны на программе без гонки данных (например, нетранзакционная запись, которая одновременно с транзакционным чтением в ту же ячейку памяти является гонкой данных).

Дополнительная информация: http://gcc.gnu.org/wiki/TransactionalMemory

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