Russian Qt Forum
Ноябрь 22, 2024, 23:50 *
Добро пожаловать, Гость. Пожалуйста, войдите или зарегистрируйтесь.
Вам не пришло письмо с кодом активации?

Войти
 
  Начало   Форум  WIKI (Вики)FAQ Помощь Поиск Войти Регистрация  

Страниц: 1 [2] 3 4 ... 7   Вниз
  Печать  
Автор Тема: AtomicData или механизм атомарной операции записи данных  (Прочитано 45269 раз)
brankovic
Гость
« Ответ #15 : Март 26, 2011, 16:25 »

А за счёт чего достигается атомарность копирования shared_ptr? Вот например:
Код
C++ (Qt)
shared_ptr<Data> ptr;
DataManager dataManager;
...
ptr = dataManager.data();
 
При копировании, на сколько я понял должно произойти как минимум следующее:
ptr - должен уменьшить свой счётчик ссылок и если он окажется 0 удалить сам объект,
скопировать указатель на новые данные и опять увеличить счётчик ссылок.
Наверное я чегото не допонимаю в этом, почему оно атомарно? 

Только за счёт мьютекса, в данном примере. Дальше боюсь запутать, пытаясь домыслить неправильно поставленный вопрос.
Записан
Igors
Джедай : наставник для всех
*******
Offline Offline

Сообщений: 11445


Просмотр профиля
« Ответ #16 : Март 26, 2011, 16:46 »

Только за счёт мьютекса, в данном примере. Дальше боюсь запутать, пытаясь домыслить неправильно поставленный вопрос.
Улыбающийся Попробую я пояснить

Код
C++ (Qt)
shared_ptr<Data> ptr;
DataManager dataManager;
...
for (int i = 0; i < 100 ++i) {
ptr = dataManager.data();
...
}
 
Вполне возможно что напр после первых 10 итераций цикла др. нитка поставит др. data. В этом случае следующие итерации цикла будут работать уже с новыми данными. Это "атомарно" и будет работать корректно. Однако это совсем не значит что данные будут изменены для того кто их уже получил (до замены) с помощью data() - что получил, с тем и работает
Записан
m_ax
Джедай : наставник для всех
*******
Offline Offline

Сообщений: 2095



Просмотр профиля
« Ответ #17 : Март 26, 2011, 19:36 »

Только за счёт мьютекса, в данном примере. Дальше боюсь запутать, пытаясь домыслить неправильно поставленный вопрос.
Улыбающийся Попробую я пояснить

Код
C++ (Qt)
shared_ptr<Data> ptr;
DataManager dataManager;
...
for (int i = 0; i < 100 ++i) {
ptr = dataManager.data();
...
}
 
Вполне возможно что напр после первых 10 итераций цикла др. нитка поставит др. data. В этом случае следующие итерации цикла будут работать уже с новыми данными. Это "атомарно" и будет работать корректно. Однако это совсем не значит что данные будут изменены для того кто их уже получил (до замены) с помощью data() - что получил, с тем и работает

Да, это я понял) Всмысле, это вполне нормальное поведение (кто успел - тот съел Улыбающийся )

А за счёт чего достигается атомарность копирования shared_ptr? Вот например:
Код
C++ (Qt)
shared_ptr<Data> ptr;
DataManager dataManager;
...
ptr = dataManager.data();
 
При копировании, на сколько я понял должно произойти как минимум следующее:
ptr - должен уменьшить свой счётчик ссылок и если он окажется 0 удалить сам объект,
скопировать указатель на новые данные и опять увеличить счётчик ссылок.
Наверное я чегото не допонимаю в этом, почему оно атомарно? 

Только за счёт мьютекса, в данном примере. Дальше боюсь запутать, пытаясь домыслить неправильно поставленный вопрос.

Хорошо, тогда получается, что сама по себе операция копиррования и присваивания двух shared_ptr (я иммею в виду без участия DataManager) не является атомарной?
А, если является, то это обеспечивается собственным мьютексом в самом shared_ptr? Тогда, если это так, то какой смысл в Read(Write)Locker в классе DataManager?

И ещё вопрос: В классе DataManager используется shared_ptr только лишь как оптимизация? Ведь аналогичный эффект получится если просто использовать непосредственно Data? 
Записан

Над водой луна двурога. Сяду выпью за Ван Гога. Хорошо, что кот не пьет, Он и так меня поймет..

Arch Linux Plasma 5
brankovic
Гость
« Ответ #18 : Март 26, 2011, 20:23 »

Хорошо, тогда получается, что сама по себе операция копиррования и присваивания двух shared_ptr (я иммею в виду без участия DataManager) не является атомарной?

да (не является атомарной для любого разумного определения атомарности)

Edit: собственно термин "атомарный" допускает трактовки. Атомарная функция или operator= -- это непонятно что, надо ещё определение дать функциональной атомарности.

И ещё вопрос: В классе DataManager используется shared_ptr только лишь как оптимизация? Ведь аналогичный эффект получится если просто использовать непосредственно Data? 

да

Как пример, такие DataManager-ы используется для классов хранения сеттингов (настройки программы), они могут быть довольно развесистыми. Рабочий тред пользуется сеттингами, но он 1) не должен их на долго лочить, 2) должен видеть консистентное состояние настроек. Такая конструкция с shared_ptr позволяет ускорить захват сеттингов. Но необходимости в shared_ptr здесь нет, просто он копируется на порядок быстрее, чем какой-нибудь std::map <std::string, std::vector <boost::any> >.
« Последнее редактирование: Март 26, 2011, 20:37 от brankovic » Записан
m_ax
Джедай : наставник для всех
*******
Offline Offline

Сообщений: 2095



Просмотр профиля
« Ответ #19 : Март 26, 2011, 20:27 »

Хорошо, тогда получается, что сама по себе операция копиррования и присваивания двух shared_ptr (я иммею в виду без участия DataManager) не является атомарной?

да

И ещё вопрос: В классе DataManager используется shared_ptr только лишь как оптимизация? Ведь аналогичный эффект получится если просто использовать непосредственно Data? 

да

Как пример, такие DataManager-ы используется для классов хранения сеттингов (настройки программы), они могут быть довольно развесистыми. Рабочий тред пользуется сеттингами, но он 1. не должен их лочить, 2. должен видеть консистентное состояние настроек. Такая конструкция с shared_ptr позволяет ускорить захват сеттингов и тем самым минимизировать трение в локах. Но необходимости в shared_ptr здесь нет, просто он копируется на порядок быстрее, чем какой-нибудь std::map <std::string, std::vector <boost::any> >.

Ясно  Улыбающийся Спасибо)
Записан

Над водой луна двурога. Сяду выпью за Ван Гога. Хорошо, что кот не пьет, Он и так меня поймет..

Arch Linux Plasma 5
vregess
Гость
« Ответ #20 : Март 27, 2011, 11:42 »

В дополнение ко всему сказанному

Не понял Вашей задумки: m_data "улетела" (из метода data) и тот кто ее подхватит будет использовать указатель на ее данные. Этот "улетевший" указатель не будет обновлен после setData, в чем тогда смысл?

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

Код:
class Data {}

class DataManager
{
public:
    ...

    // Раньше было data()
    SharedPointer<Data> getDataSnapshot() const
    {
         ReadLocker lock;
         return m_data;
    }
    ...
}

Здесь мне видится один недостаток: после получения SharedPointer<Data> у нас будет возможность менять данные по указателю. Но с практической точки зрения работать будет быстрее, чем делать класс Data синхронизированным. Да и обойти это можно в принципе.
Записан
Igors
Джедай : наставник для всех
*******
Offline Offline

Сообщений: 11445


Просмотр профиля
« Ответ #21 : Март 27, 2011, 11:53 »

Такой подход можно сравнить с получением текущего снапшота данных. Для многих задач этого будет достаточно. Наверное будет понятнее, если в примере просто поменять название метода:
Это ясно, но вот "для многих" вызывает сомнения. На мой взгляд число таких задач намного меньше по сравнению с обычной синхронизацией запись/чтение

Здесь мне видится один недостаток: после получения SharedPointer<Data> у нас будет возможность менять данные по указателю. Но с практической точки зрения работать будет быстрее, чем делать класс Data синхронизированным. Да и обойти это можно в принципе.
Частенько "в принципе" просто означает "никогда"  Улыбающийся
« Последнее редактирование: Март 27, 2011, 13:27 от Igors » Записан
vregess
Гость
« Ответ #22 : Март 27, 2011, 13:06 »

Это ясно, но вот "для многих" вызывает сомнения. На мой взгляд число таких задач намного меньше по сравнению с обычной синхронизацией запись/чтение
Да, может и так. Видимо по этому чаще встречается описание примеров именно с  обычной синхронизацией запись/чтение.

Частенько "в принципе" просто означает "никогда"  Улыбающийся

Ну я имел ввиду, что это возможно. Простой пример:
Цитировать
void SomeClass::foo()
{
    SharedPointer<Data> data = cfg.getDataSnapshot();
    Data *ptr = data.get();
    assert(prt);
    doStuff(*ptr);
}

void SomeClass:doStuff(const Data &data)
{
   ...
}
Писанины больше, но и скорость выполнения тоже. Иногда это оправдано, иногда - нет.
Записан
Akon
Гость
« Ответ #23 : Март 27, 2011, 15:21 »

В дополнение ко всему сказанному

Не понял Вашей задумки: m_data "улетела" (из метода data) и тот кто ее подхватит будет использовать указатель на ее данные. Этот "улетевший" указатель не будет обновлен после setData, в чем тогда смысл?

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

Код:
class Data {}

class DataManager
{
public:
    ...

    // Раньше было data()
    SharedPointer<Data> getDataSnapshot() const
    {
         ReadLocker lock;
         return m_data;
    }
    ...
}

Здесь мне видится один недостаток: после получения SharedPointer<Data> у нас будет возможность менять данные по указателю. Но с практической точки зрения работать будет быстрее, чем делать класс Data синхронизированным. Да и обойти это можно в принципе.



Для большей строгости можно использовать const propagation:
Код:

...  
     const SharedPointer<Data> getDataSnapshot()
    {
         ReadLocker lock;
         return m_data;
    }

    const SharedPointer<const Data> getDataSnapshot() const
    {
         return const_cast<DataManager*>(this)->getDataSnapshort();
    }
...

Соответственно, у кого const ссылка на DataManager не может изменить состояние данных.
Записан
brankovic
Гость
« Ответ #24 : Март 27, 2011, 18:59 »

Для большей строгости можно использовать const propagation:
...
Соответственно, у кого const ссылка на DataManager не может изменить состояние данных.

а почему не сделать так:

Код
C++ (Qt)
   const SharedPointer<const Data> getDataSnapshot () const
   {
        ReadLocker lock;
        return m_data;
   }
 

в чём смысл второго метода с не-конст указателем, если полученный объект нельзя менять всё равно?
Записан
vregess
Гость
« Ответ #25 : Март 27, 2011, 19:10 »


Для большей строгости можно использовать const propagation:

Да, я перемудрил. Так лучше.
А еще можно мьютекс сделать mutable и оставить только константный getDataSnapshot()

Код:

...  
     SharedPointer<const Data> getDataSnapshot() const
    {
         ReadLocker lock(m_mutex);
         return m_data;
    }

private:
    mutable Mutex m_mutex;


brankovic уже подметил
« Последнее редактирование: Март 27, 2011, 19:23 от ck » Записан
Akon
Гость
« Ответ #26 : Март 27, 2011, 19:51 »

Для большей строгости можно использовать const propagation:
...
Соответственно, у кого const ссылка на DataManager не может изменить состояние данных.

а почему не сделать так:

Код
C++ (Qt)
   const SharedPointer<const Data> getDataSnapshot () const
   {
        ReadLocker lock;
        return m_data;
   }
 

в чём смысл второго метода с не-конст указателем, если полученный объект нельзя менять всё равно?

почему полученный объект (Data) нельзя менять?
Записан
Igors
Джедай : наставник для всех
*******
Offline Offline

Сообщений: 11445


Просмотр профиля
« Ответ #27 : Март 27, 2011, 20:30 »

Если привлекается LockRead, то вся конструкция теряет смысл (какой же это lock-free?). Практичнее прикинуть какой read/write локер пользовать.
Записан
vregess
Гость
« Ответ #28 : Март 27, 2011, 20:44 »

почему полученный объект (Data) нельзя менять?

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

И, кстати, const SharedPointer тут не важен, главное чтоб const Data был.

Если привлекается LockRead, то вся конструкция теряет смысл (какой же это lock-free?). Практичнее прикинуть какой read/write локер пользовать.

Да мы вроде уже не о lock-free.
Записан
Akon
Гость
« Ответ #29 : Март 27, 2011, 21:07 »

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

Цитировать
И, кстати, const SharedPointer тут не важен, главное чтоб const Data был.
Это нужно для явной семантики присваивания. Например, без const можно было бы написать getDataSnapshot().reset(newData) или getDataSnapshot() = somePointer, что совершенно бесполезно, поскольку объект временный. Короче, нет никакого смысла менять временный объект, и для предотвращения подобных действий объект возвращается как константный.
Записан
Страниц: 1 [2] 3 4 ... 7   Вверх
  Печать  
 
Перейти в:  


Страница сгенерирована за 0.259 секунд. Запросов: 23.