2015-06-16 3 views
5

Я играю с std::atomic, но я думаю, что я не совсем понял концепцию. Мне интересно, почему нет атомных контейнеров. Поэтому я немного поиграл. Сначала я попробовал следующее:C++ атомный список контейнеров

std::atomic<std::list<int> > atomicList; 

Но, как уже некоторые другие люди отметили, что это не работает, потому что конструктор noexcept. Так что я создал какой-то хак:

template<class T> 
class MyList 
{ 
public: 
    //Make sure that no exception is thrown 
    MyList() noexcept 
     try: l() 
    {}catch(...) {} 

    void push_back(const T &t) { l.push_back(t); } 

    void pop_front() { l.pop_front(); } 

    size_t size() const { return l.size(); } 

private: 
    list<T> l; 

}; 

atomic<MyList<int> > atomicList; 

Теперь я работал с ним, но я обнаружил, что он не работает должным образом, и я получаю ошибки ошибки сегментации.

Может кто-нибудь объяснить, почему это невозможно создать атомный список?

EDIT: Если кто-то хочет увидеть, как моя тестовая программа действительно выглядит для лучшего понимания:

#include <list> 
#include <thread> 
#include <sys/time.h> 
#include <iostream> 
#include <atomic> 

using namespace std; 

template<class T> 
class MyList 
{ 

public: 
    MyList() noexcept 
     try: l() 
    {}catch(...) {} 

    void push_back(const T &t) { l.push_back(t); } 

    void pop_front() { l.pop_front(); } 

    size_t size() const { return l.size(); } 

private: 
    list<T> l; 

}; 

atomic<MyList<int> > l; 

void work() 
{ 
    for(unsigned int i = 0; i < 100000; ++i) 
    { 
     //Called operator() 
     ((MyList<int>&)l).push_back(i); 
     ((MyList<int>&)l).push_back(((MyList<int>&)l).size()); 
     ((MyList<int>&)l).pop_front(); 
    } 
} 

int main(int argc, char *args[]) 
{ 
    struct timeval time1; 
    struct timeval time2; 
    gettimeofday(&time1, 0); 
    thread t1(work); 
    thread t2(work); 
    thread t3(work); 
    thread t4(work); 
    t1.join(); 
    t2.join(); 
    t3.join(); 
    t4.join(); 
    gettimeofday(&time2, 0); 
    cout<<((time2.tv_sec-time1.tv_sec)+double(time2.tv_usec-time1.tv_usec)/1000000)<<endl; 
} 

ответ

4

Первая и самая важная проблема: это не может работать. Вам нужна синхронизация вокруг выполнения функций-членов, а не для извлечения списка. std::atomic даже не начинает напоминать то, что вам нужно.

Что касается вашей попытки внедрения, то отличное от atomic<T> до T& не может сделать ничего разумного.

И даже если бы это было значимо, такой бросок полностью забыл бы об атомности вашего объекта, и поэтому все, что вы делаете со ссылками, не будет атомарным.

+0

OK Я понимаю. Так действительно ли можно * конвертировать * stl Container в атомарное? Или нужно создать его с нуля? –

+1

@Thomas: Вы могли бы попытаться написать контейнерный адаптер, но я думаю, вам будет гораздо лучше подумать с точки зрения «написать его с нуля, но, возможно, использовать стандартный контейнер внутри». Тем более, что концепция 'Container' на самом деле * невозможна * для создания атома, если только их' value_type' не является специализированным объектом, который взаимодействует с контейнером, чтобы вести себя атомарно (например, как можно было бы использовать C [i] = x; 'возможно атомная операция?). – Hurkyl

1
((MyList<int>&)l).push_back(i); 

std::atomic не обеспечивает оператора преобразования в ссылки. Если бы вы использовали static_cast, он даже не компилировался, но здесь C-cast напрямую переинтерпретирует std::atomic<MyList<int>> как MyList<int>, у которого есть веские причины не работать.

Вы не можете непосредственно изменить, что находится в std::atomic. Вам необходимо получить копию данных с помощью load(), изменить эту копию, а затем поменять ее на store().

+0

Так должно ли это работать: «MyList temp = l.load()' затем изменить 'temp' и сохранить его обратно с помощью' l.store (temp) 'Можете добавить это к своему ответу? –

+0

И еще одна вещь: Итак, что такое 'operator()', если я не могу использовать его напрямую? –

+0

@ThomasSparber да, было бы. Но, как указывает Хуркил, единственное, что предоставляет 'std :: atomic', - это операция атомной нагрузки/хранения. Он не синхронизирует то, что вы на самом деле делаете с данными, оно только гарантирует, что когда вы заменяете его на новые данные, каждый получает полный объект. Оператор преобразования преобразуется в 'T', а не' T & ', и является просто синтаксическим сахаром над' load() '. – Quentin

Смежные вопросы