2012-03-17 15 views
6

Mam zestaw funkcji w języku C++Operacje atomowe w C++

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

Teraz chcę uczynić funcA atomowy, czyli funcB i funcC połączenia wewnątrz funcA powinny być wykonywane atomowo. Czy jest jakiś sposób, aby to osiągnąć?

+1

można opracować co masz na myśli przez „wykonywane atomowo”? Wygląda na to, że chcesz po prostu 'std :: mutex'. – Potatoswatter

+1

To jest prawdopodobnie to, czego on naprawdę chce. – Joshua

+1

Może ktoś powinien pokazać mu odpowiednią składnię do użycia 'std :: mutex'. Może on nie wie. Nie wiedziałbym, jak używać muteksa w niektórych innych językach. Wyjaśnienie powinno prawdopodobnie uwzględniać założenie, że cały program musi respektować muteks. To znaczy. wątek B może nadal wywoływać funcB i funcC, nawet jeśli wątek A robi to pod mutexem, chyba że programista zabrania go przez projekt. –

Odpowiedz

6

Ogólnie NIE. Operacje atomowe są bardzo precyzyjnie określone. To, czego chcesz, to semafor lub muteks.

+0

Należy pamiętać, że chociaż ta odpowiedź jest ściśle prawidłowa, odpowiedź Robᵩ jest bardziej przydatna dla użytkownika744829. – Joshua

11

Jednym ze sposobów osiągnięcia tego jest użycie nowych (C++ 11) funkcji std::mutex i std::lock_guard.

Dla każdego chronionego zasobu tworzony jest pojedynczy globalny std::mutex; każdy wątek następnie blokuje ten mutex, gdyż wymaga, przez utworzenie 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(); 
} 

Uwagi:

  • W przykładzie jakiś kod niezwiązane funcA mogłoby wywołać albo funcB lub funcC bez uhonorowanie zablokuj zestaw funcA.
  • W zależności od tego, jak zbudowany jest Twój program, możesz chcieć inaczej zarządzać czasem życia muteksa. Na przykład może być członkiem klasy tej klasy, która obejmuje funcA.
1

Jeśli używasz GCC 4.7, niż można użyć nowego transakcyjna pamięci funkcji, aby wykonać następujące czynności:

transakcyjna pamięci jest przeznaczony do programowania z wątków prostszych, w szczególności dostępu do synchronizacji danych udostępnionych między kilkoma wątkami używającymi transakcji. Podobnie jak w przypadku baz danych, transakcja jest jednostką pracy, która kończy się w całości lub nie ma żadnego efektu (tj. Transakcje są wykonywane atomowo). Ponadto, transakcje są od siebie odizolowane, tak że każda transakcja widzi spójny widok pamięci.

Obecnie transakcje są obsługiwane tylko w C++ i C w postaci wyciągów transakcyjnych, wyrażeń transakcji i transakcji funkcji. W poniższym przykładzie, zarówno A i B zostanie odczytany, a różnica zostanie napisany do c, wszystko atomowo i odizolowany od innych transakcji:

__transaction_atomic { c = a - b; } 

Dlatego inny wątek można użyć następującego kodu do jednoczesnego aktualizowania B bez zawsze powoduje c trzymać wartość ujemną (i bez konieczności stosowania innych konstrukcji synchronizacji, takich jak śluzy lub C++ 11 atomistyki):

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

Dokładne semantyki transakcji są zdefiniowane w kategoriach C++ 11 Model pamięci/C1X (patrz poniżej link do specyfikacji). Z grubsza transakcje zapewniają gwarancje synchronizacji, które są podobne do gwarancji gwarantowanych przy użyciu pojedynczego globalnego zamka jako strażnika dla wszystkich transakcji. Zauważ, że podobnie jak inne konstrukcje synchronizacji w C/C++, transakcje opierają się na programie wolnym od danych (na przykład zapis nietransakcyjny, który jest współbieżny z transakcyjnym odczytem w tej samej lokalizacji pamięci, jest wyścigiem danych).

Więcej informacji: http://gcc.gnu.org/wiki/TransactionalMemory