Russian Qt Forum

Qt => Многопоточное программирование, процессы => Тема начата: Akon от Октябрь 30, 2011, 11:33



Название: Многопоточная запись/чтение implicitly-shared классов
Отправлено: Akon от Октябрь 30, 2011, 11:33
Часто приходится видеть такое:
Код:
QByteArray a("some data");

// write access, thread #1
void funcFromThread1()
{
a = QByteArray();  // before assignment assume ref. count == 1
...
}

// read-only access, thread #2
void funcFromThread2()
{
const QByteArray b = a;  // potential access to released memory (pointed by a.d)
...
}

В документации сказано о безопасности счетчика ссылок и о небезопасности расшаренных данных. Вероятно, у многих (в т.ч. и у меня) создается впечатление, что shallow copy
Код:
const QByteArray b = a;
безопасно в многопоточной среде, но при одновременном чтении/записи, как приведено выше, это не так.

Безопасный доступ: сериализация (мьютексы) и lock-free вариант, приведенный ниже:
Код:
// lock-free

QByteArray a("some data");

// write access, thread #1
void funcFromThread1()
{
const QByteArray tmp = a;  // ref.count >= 2 while writing a
a = QByteArray();
...
}

// read-only access, thread #2
void funcFromThread2()
{
const QByteArray b = a;  // safe access while writing a
...
}

имхо, как-то стоило бы отразить эту проблему в документации, например, в boost подобное использование разжевано для boost::shared_ptr.


Название: Re: Многопоточная запись/чтение implicitly-shared классов
Отправлено: Igors от Октябрь 30, 2011, 14:01
Ну Вы бы уж "разжевали" для широкого круга читателей напр
Код
C++ (Qt)
QByteArray &QByteArray::operator=(const QByteArray & other)
{
   other.d->ref.ref();
   if (!d->ref.deref())
       qFree(d);
   d = other.d;
   return *this;
}
 
Читающая нитка может вклиниться перед qFree и рухнуть. Чтобы исключить такое дело надо запастись счетчиком ссылок >= 2. Добавим что читающий может получить как новые так и старые данные - как получится


Название: Re: Многопоточная запись/чтение implicitly-shared классов
Отправлено: Akon от Октябрь 30, 2011, 19:43
Да, спасибо за дополнение-"разжевку".


Название: Re: Многопоточная запись/чтение implicitly-shared классов
Отправлено: Авварон от Октябрь 30, 2011, 20:03
Igors
присваивание шаред данных атомарно. никто никуда вклинится не может.


Название: Re: Многопоточная запись/чтение implicitly-shared классов
Отправлено: Igors от Октябрь 30, 2011, 20:58
присваивание шаред данных атомарно. никто никуда вклинится не может.
Уголовники показывают костяшку домино 5/6 и говорят "выбирай". Что выберет умный человек?  :) Вот также и читающая нитка 


Название: Re: Многопоточная запись/чтение implicitly-shared классов
Отправлено: andrew.k от Октябрь 30, 2011, 21:02
Поясни про уголовников?)


Название: Re: Многопоточная запись/чтение implicitly-shared классов
Отправлено: Igors от Октябрь 30, 2011, 21:08
Код
C++ (Qt)
   if (!d->ref.deref())
                                  <-- пробой здесь
       qFree(d);
 


Название: Re: Многопоточная запись/чтение implicitly-shared классов
Отправлено: Авварон от Октябрь 30, 2011, 21:11
2 нитки _не могут_ владеть 1й копией. у каждой нитки свой d.


Название: Re: Многопоточная запись/чтение implicitly-shared классов
Отправлено: Akon от Октябрь 30, 2011, 21:50
Это так, до поры
Код:
d = other.d;

Про уголовников тоже интересно.


Название: Re: Многопоточная запись/чтение implicitly-shared классов
Отправлено: andrew.k от Октябрь 30, 2011, 21:52
2 нитки _не могут_ владеть 1й копией. у каждой нитки свой d.
А почему? В том смысле, где это написано?

Qt uses an optimization called implicit sharing for many of its value class, notably QImage and QString. Beginning with Qt 4, implicit shared classes can safely be copied across threads, like any other value classes. They are fully reentrant. The implicit sharing is really implicit.
Threads and Implicitly Shared Classes (http://doc.crossplatform.ru/qt/4.6.x/threads-modules.html)
Какой из этого вывод?


Название: Re: Многопоточная запись/чтение implicitly-shared классов
Отправлено: Авварон от Октябрь 30, 2011, 22:04

andrew.k
не, это я туплю просто.


Название: Re: Многопоточная запись/чтение implicitly-shared классов
Отправлено: Akon от Октябрь 30, 2011, 22:10
Ну там дальше написано
Цитировать
...  Atomic reference counting does, however, guarantee that a thread working on its own, local instance of an implicitly shared class is safe. ...
Но проблема возникает как раз при переустановке своего d.

Авварон
у вас создалось впечатление  :)


Название: Re: Многопоточная запись/чтение implicitly-shared классов
Отправлено: andrew.k от Октябрь 30, 2011, 22:10
Тогда, что получается по теме? я не очень понимаю.
Нужно использовать мютексы при доступе к QByteArray? Потому что без них ерунда какая-то получается.
Мыль ТС я не очень понял. Точнее приведенный там код.


Название: Re: Многопоточная запись/чтение implicitly-shared классов
Отправлено: andrew.k от Октябрь 30, 2011, 22:12
Ну там дальше написано
Цитировать
...  Atomic reference counting does, however, guarantee that a thread working on its own, local instance of an implicitly shared class is safe. ...
Но проблема возникает как раз при переустановке своего d.

Получается другой поток в принципе не увидит изменений сделанных другим потоком.


Название: Re: Многопоточная запись/чтение implicitly-shared классов
Отправлено: Igors от Октябрь 31, 2011, 09:55
Код
C++ (Qt)
QByteArray &QByteArray::operator=(const QByteArray & other)
{
   other.d->ref.ref();
   if (!d->ref.deref())
       qFree(d);
   d = other.d;
   return *this;
}
 
Допустим d->ref.defref() вернул false и в этот момент ОС передал управление др. нитке. Конечно d = other.d выполнится атомарно, но это будет потом, когда текущая нитка снова получит управление. А сейчас в читающей нитке это d пройдет как член other, ну читатель увеличит счетчик ref и присвоит указатель d своему. Может даже успеет что-то сделать пока проснувшаяся нитка не выполнит qFree  :)


Название: Re: Многопоточная запись/чтение implicitly-shared классов
Отправлено: andrew.k от Октябрь 31, 2011, 13:45
Код
C++ (Qt)
QByteArray &QByteArray::operator=(const QByteArray & other)
{
   other.d->ref.ref();
   if (!d->ref.deref())
       qFree(d);
   d = other.d;
   return *this;
}
 
Конечно d = other.d выполнится атомарно,
Так мы же и так находимся внутри operator=, который должен выполнится атомарно?
Или атомарно выполняется только operator= внутри другого operator=? )


Название: Re: Многопоточная запись/чтение implicitly-shared классов
Отправлено: Akon от Октябрь 31, 2011, 14:25
Цитировать
Тогда, что получается по теме? я не очень понимаю.
Нужно использовать мютексы при доступе к QByteArray? Потому что без них ерунда какая-то получается.
Мыль ТС я не очень понял. Точнее приведенный там код.
Для данного юзкейса (simultaneously reading/writing) сериализованный доступ (мьютексы) один из вариантов.
Кто такой ТС?

Цитировать
Получается другой поток в принципе не увидит изменений сделанных другим потоком.
Нет, увидит.


Название: Re: Многопоточная запись/чтение implicitly-shared классов
Отправлено: andrew.k от Октябрь 31, 2011, 15:10
ТС - топик стартер. Тот, кто начал тему.