2015-02-27 2 views
0

я иметь следующую иерархию:Является ли это использование атома правильным?

struct Point 
{ 
    std::atomic<int> x; 
    std::atomic<int> y; 
    std::atomic<int> z; 
} 

class Data 
{ 
std::atomic<int> version; 
Point point; 
} 

unordered_map<std::string,Data> hashtable; // global 

Резьба1 является производителем:

Thread 1 получают std::pair<std::string,Point> new_data, он делает:

shared_hashtable[new_data.first].point.x.store(new_data.second.x,memory_order_release) // update X 
shared_hashtable[new_data.first].point.y.store(new_data.second.y,memory_order_release) // update Y 
shared_hashtable[new_data.first].point.z.store(new_data.second.z,memory_order_release) // update Z 
hashtable[new_data.first].version.store(hashtable[new_data.first].version+1,memory_order_release) // update the version of the data 

Тема 2 делает:

int local_version; 
while(local_version!=shared_hashtable["string1"].version.load(memory_order_acquire)) 
{ 
    //...process data... 
local_version=shared_hashtable["string1"].version.load(memory_order_acquire); 
} 

Выполняет ли заказ памяти гарантию в моем магазине и загрузка не будут переупорядочены. Работает ли проект так, как ожидалось: если обновляется объект в hastable["string1"], обрабатываю ли я соответствующие данные в потоке 2?

+2

Я не вижу дизайн whatsoev er, вы играете параллелизм на элементарной сфере, что не хорошо. –

+0

Ну, я хочу, чтобы глобальный объект обновлялся, а другой поток мог улавливать эти обновления. Я пытаюсь сделать это, не создавая новый объект каждый раз, просто обновляя существующий. – user2164703

ответ

2

hashtable не защищен от одновременного доступа, а его operator[] не является const. Если вы гарантируете, что (1) hashtable["string1"] вставлен в таблицу до того, как эти два потока попытаются получить к ней доступ, и (2) никакие другие потоки не записывают в hashtable за время, когда эти два потока запущены, поиск в hashtable не приведет к в гонке данных. Если вы не можете гарантировать (1) и (2), вам необходимо защитить поиск с помощью мьютекса. unordered_map аннулирует итераторы при повторном обращении, но ссылки остаются действительными до тех пор, пока ссылочный элемент не будет удален с карты.

модель Память гарантирует вам, что записи в потоке 1, которые предшествуют memory_order_release записи в version видны нити 2 после того, как он memory_order_acquire считывает соответствующее значение из version. Это было бы так, даже если доступ к членам Point был неатомным.

Тем не менее, возможно, что читает из Point членов в нити 2 значения см от позже записи в потоке 1, так что вы не гарантированы, что три Point членов читать нить 2 соответствуют конкретной Point на самом деле это было написано нитью 1. Я полагаю, вам нужна гарантия того, что Point, обработанный потоком 2, фактически представляет собой Point, написанный нитью 1, а не совокупность значений нескольких разных точек (например, x от версии 1, y от версии 2, z из версии 3). Изменение дизайна от

struct Point { atomic<int> x, y, x; }; 

в

struct Point { int x, y, x; }; 
struct Data { 
    atomic<int> version; 
    atomic<Point> point; 
}; 

гарантирует, что Point чтения на самом деле Point написано. Конечно, не обязательно Point, который соответствует данному version: point может быть уже перезаписан позже версии в потоке 1 по времени, когда поток 2 приближается к его чтению. Это может привести к тому, что одна и та же точка обрабатывается дважды потоком 2, один раз с устаревшей версией и снова на более поздней итерации с соответствующей версией. Если вам нужно убедиться, что каждая версия обработана не чаще одного раза, вы должны убедиться, что version - это то же самое после, читающее point как было до чтение point.

Собираем все вместе, мы получаем эту программу (DEMO):

struct Point 
{ 
    int x, y, z; 
}; 

struct Data 
{ 
    std::atomic<int> version; 
    std::atomic<Point> point; 
}; 

std::unordered_map<std::string, Data> hashtable; 
std::mutex mtx; 

Data& lookup(const std::string& key) { 
    std::lock_guard<std::mutex> guard{mtx}; 
    return hashtable[key]; 
} 

void thread1() { 
    std::string key; 
    Point point; 
    std::tie(key, point) = // get new key/point pair 
    auto& ref = lookup(key); 
    ref.point.store(point, std::memory_order_relaxed); 
    ref.version.fetch_add(1, std::memory_order_release); 
} 

void thread2() { 
    auto& ref = lookup("string1"); 
    int local_version = 0; 
    for (;;) { 
    auto const version = ref.version.load(std::memory_order_acquire); 
    if (local_version != version) { 
     auto point = ref.point.load(std::memory_order_relaxed); 
     if (version == ref.version.load(std::memory_order_acquire)) { 
     local_version = version; 
     // ...process point... 
     } 
    } 
    } 
} 
+0

Чтобы быть уверенным, вы используете std :: memory_order_relaxed для загрузки ref.point, видя остальное, разве это не будет order_acquire? – user2164703

1

Вы можете попробовать https://github.com/Taymindis/atomic_hashtable

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

API Документы указаны в Readme.md

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