LINUX.ORG.RU

Нам это рассказывали на первом семестре. Если прогуливал - почитай ... хотя-бы Вирта.

naryl ★★★★★
()

> Что лучше использовать?

stl - чем меньше необязательных привязок к сторонним библиотекам тем лучше

lester ★★★★
()
Ответ на: комментарий от lester

Плюсую. Но если уж связался с кути, то используй их на полную.

tia
()

Лучше конечно Qt-контейнеры, поскольку они Implicitly Shared. Ссылки можно кидать прямо между потоками, при этом разделение данных происходит только при попытке изменения. Операции копирования для всех Qt-контейнеров атомарны, экономится память, ресурсы CPU, код полностью свободен от механизмов синхронизации, вроде мутексов. Экземпляры Qt-контейнеров можно ложить в сигналы или пускать по событиям, не боясь в какой поток сигнал или событие прийдёт.

Более того, если набить собственную структуру кучей Qt-контейнеров, то время выполнения Copy-On-Write для такой структуры будет равно времени Copy-On-Write только для того поля, которое вы меняете. Если эту же структуру набить STL-контейнерами - время отделения будет равно сумме времени копирования всех полей, даже если вы захотели поменять в структуре всего один байт.

Dendy ★★★★★
()
Ответ на: комментарий от Dendy

Я задам глупый вопрос, но: А как тогда идёт синхронизация ресурсов(самих контейнеров) между потоками? Как оно работает?

tia
()
Ответ на: комментарий от tia

У каждого экземпляра счётчик ссылок, который автомарной операцией может меняться. Когда выполняется мутабельная операция - этот счётчик проверяется, и если не равен единице - делается глубокая копия. Процесс глубокой копии безопасен, так как экземпляры в других потоках не смогут изменить содержимое, поскольку для них счётчик ссылок до окончания процесса копирования всегда будет больше 1. После разделения у новой копии всегда количество ссылок 1, что значит, никто из других потоков физически не может ссылаться на обьект, а значит его можно менять. При этом в процессе копирования остальные обьекты могут свободно читать данные.

Суть такова - если у вас есть экземпляр QString - это ваша логическая копия и неважно сколько и из каких потоков на эту же строку ссылаются - вы можете делать с ней всё что угодно. Такой подход на порядок безопастней, поскольку убирает необходимость работы с памятью и мутексами.

Также Qt-контейнеры позволяют забыть про возвращение константных ссылок из классов, и при этом следить, чтобы пока ты работает с этой ссылкой обьект-хозяин не дай бог не измнил возвращённый экземпляр. В Qt можно попросту возвратить не ссылку, а сам экземпляр.

Dendy ★★★★★
()

Контейнеры STL технически не могут выполнять задачи контейнеров Qt. И дело даже не в отсутствии реализации такой же многопоточности. Даже если кто-то завтра создаст реализацию контейнеров с интерфейсом STL и поведением Qt - это будет сизифов труд, поскольку проблема в предопределённом поведении STL. Вы попросту не сможете использовать эту библиотеку STL Qt Edition, поскольку она будет конфликтовать с кодом, написаным для обычного STL. Да и как вы обьясните другим, что с одной стороны программа совместима с STL на уровне исходного кода, а с другой стороны, чтобы её собрать нужна левая реализация этого STL, бинарно несовместимая с оригиналом? С Qt же вы можете как угодно мешать оба типа контейнеров в одной программе без опасений.

Dendy ★★★★★
()

>В Linux как минимум 2 stl - в gcc и stlport.

В каждом MSVC - своя.

В Builder на выбор кажется 3 штуки



Вывод - если вы хотите настоящую кроссплатформенную прогу - юзайте QT STL. Портируемости будет больше.

Kristi
()
Ответ на: комментарий от Dendy

Что курил автор этого текста?

anonymous
()
Ответ на: комментарий от Dendy

Для троллей http://doc.trolltech.com/4.6/threads-reentrancy.html

Many Qt classes are reentrant, but they are not made thread-safe, because making them thread-safe would incur the extra overhead of repeatedly locking and unlocking a QMutex. For example, QString is reentrant but not thread-safe. You can safely access different instances of QString from multiple threads simultaneously, but you can't safely access the same instance of QString from multiple threads simultaneously (unless you protect the accesses yourself with a QMutex).

http://doc.trolltech.com/4.6/qstring.html

All functions in this class are reentrant, except for ascii(), latin1(), utf8(), and local8Bit(), which are nonreentrant.

QString не является потокобезопасным, в нём не используются блокировки. Безопасно работать в каждом потоке с отдельным экземпляром, если не использовать самостоятельно мьютексы. Аналогичная ситуация с std::string.

anonymous
()

Qt появилось тогда, когда ещё не было STL. Вот и наплодило сущности.

anonymous
()
Ответ на: комментарий от tia

> А как тогда идёт синхронизация ресурсов(самих контейнеров) между потоками? Как оно работает?

Доступ к одному и тому же объекту из разных потоков должен синхронизироваться с помощью мьютексов, спинлоков и пр., если в документации явно не сказано, что класс thread-safe. Qt'шное неявноее разделение данных не имеет никакого отношения к обеспечению потокобезопасности.

mannaz
()
Ответ на: комментарий от naryl

Пизди^WВрешь, не было у нас этого в 1-м семестре. Может на втором курсе и рассказывали, но я на лекциях не был и судя по уровню того, что было на тех лекциях на которых я был, не могли там таких вещей рассказывать.

Dark_SavanT ★★★★★
()
Ответ на: комментарий от anonymous

QString не является потокобезопасным, в нём не используются блокировки. Безопасно работать в каждом потоке с отдельным экземпляром, если не использовать самостоятельно мьютексы. Аналогичная ситуация с std::string.

Обьясняю. Потокобезопастными являются не классы, а функции/методы. Если все методы в классе являются потокобезопастными, то и весь класс для удобства называют потокобезопастными. Теперь как это работает в Qt-контейнерах на примере того же QString:

Потокобезопастными являются все без исключения:

  • копирующие конструкторы
  • операторы присвоения
  • деструкторы
  • операторы явного отделения detach()

Это значит, что отделив одну логическую копию от другой между потоками, внутри самого потока с экземпляром можно делать всё что угодно, поскольку все функции - reentrant. И совершенно неважно, что несколько экземпляров в разных потоках ссылаются на одни и те же данные.

Теперь по поводу:

All functions in this class are reentrant, except for ascii(), latin1(), utf8(), and local8Bit(), which are nonreentrant.

Присмотритесь. Эти функции не являются частью Qt4 API, они выделены красным. Это режим совместимости с Qt3, функции находятся в Qt3Support. Эти несколько функций и не могут быть reentrant(), поскольку не позволяются безопастно отделить экземпляр, так как содержимое мультибайтной кодировки больше не хранится в Qt4 QString в отличии от Qt3 QString. Забудьте про них, как про страшный сон.

Dendy ★★★★★
()
Ответ на: комментарий от legolegs

Ну, тролльтек, не столь важно. Вирт тут как замешан?

melkor217 ★★★★★
()
Ответ на: комментарий от mannaz

Доступ к одному и тому же объекту из разных потоков должен синхронизироваться с помощью мьютексов, спинлоков и пр., если в документации явно не сказано, что класс thread-safe. Qt'шное неявноее разделение данных не имеет никакого отношения к обеспечению потокобезопасности.

Вам нужно подучить матчасть, прежде чем дезинформировать других. А лучше почитать исходники, тоже хорошая практика.

Implicit Sharing

Threads and Implicitly Shared Classes

Reentrancy and Thread-Safety

QSharedDataPointer Class Reference

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.

Dendy ★★★★★
()
Ответ на: комментарий от Dendy

P.S. Вообще я уже не удивляюсь, почему столько людей постоянно спрашивают чем Qt-контейнеры отличаются от STL, а некоторые с разгону кричат «велосипед». Ответ как всегда: RTFM!

Dendy ★★★★★
()
Ответ на: комментарий от Dendy

> Вам нужно подучить матчасть, прежде чем дезинформировать других. А лучше почитать исходники, тоже хорошая практика.

Не торопись задирать носик, детка. В вопросе, на который отвечал я, речь шла о синхронизации доступа к одному и тому же объекту, а не к его различным копиям. Ты, как какой-то неадекват, в ответ начал рассказывать о том, как замечательно и атамарно работает банальный счетчик ссылок. Теперь для примера расскажи мне, как copy-on-write поможет тебе безопасно добавлять элементы в общий список из разных потоков?

потокобезопастными


Я бы на твоем месте постеснялся использовать слово «подучить» в принципе.

mannaz
()
Ответ на: комментарий от mannaz

То что вы думали написать и то что написали - разные вещи. Говоря о доступе уточнили бы, что имеются в виду мутабельные операции, а то ведь совершенно непонятно.

Так вот, поскольку в контейнерах (не только Qt, а и STL) предусмотрено разделение данных (другими словами - копирующий конструктор) значит, что данные обьекта неуникальны и мутабельные операции на обьектах не могут гарантировать изменение целевого обекта в принципе, поскольку в принципе невозможно знать данные какого обьекта меняются. Эта схема идёт вразрез с идеей общих данных, неважно, поддерживают ли они copy-on-write или нет, и решается, как вы верное заметили, самостоятельными механизмами многопоточной синхронизации.

Dendy ★★★★★
()
Ответ на: комментарий от Dendy

Ты со своим хоботом обкурился сильно.

Класс QString не является потокобезопасным. С его экземпляром нельзя работать из разных потоков одновременно. Мьютексы в нём не используются из соображений производительности.

Но можно работать из одного потока с одим экземпляром, потому что функции QString не изменяют глобальные данные для своей работы, что являеся истиной также и для std::string, vector, map и т.п.

Операторы присваивания, копирующие конструкторы, деструкторы и т.п. не являются потокобезопасными у QString.

Не вводи людей в заблуждение.

anonymous
()
Ответ на: комментарий от Dendy

> Говоря о доступе уточнили бы, что имеются в виду мутабельные операции

Не вижу смысла рассуждать о том, что операции, не изменяющие никаких разделяемых данных, должны быть потокобезопасными. Если для кого-то это является важной фичей, то ради бога.

mannaz
()
Ответ на: комментарий от Dendy

>Потокобезопастными являются не классы, а функции/методы.

Это вообще шедевр.

Потокобезопасным может быть только сам класс как единое целое вместе со всеми своими функциями.

anonymous
()
Ответ на: комментарий от naryl

> Нам это рассказывали на первом семестре. Если прогуливал - почитай ... хотя-бы Вирта.

Я геолог, у нас программирования не было толком.

Obey-Kun ★★★★★
() автор топика
Ответ на: комментарий от anonymous

Хотя операторы присваивания, копирующие конструкторы, деструкторы у QString действительно потокобезопасны. Пишут в книге «Qt4: программррвание GUI на C++», что операции со счётчиками ссылок выполняют атомарно и поэтому при изменении данных какой-то функцией она сделает копию объекта, так как копирующий конструктор для начала увеличит счётчик на данные.

Но это всё значит геморрой вместо простоты STL, где запись в вектор (например vector<int>) выполняется в встроенном виде (inline) за 1 команду процессора без вызова функций и без каких бы то ни было дополнительных действий.

anonymous
()
Ответ на: комментарий от anonymous

Операторы присваивания, копирующие конструкторы, деструкторы и т.п. не являются потокобезопасными у QString.

Вы ошибаетесь.

Все методы контейнеров в Qt - reentrant. Потокобезопастность достигается за счёт потокобезопастных (прошу прощение за тавтологию) конктрукторов, деструкторов и операций присваивания и дальнейшую работу с экземпляром как с копией. Изменение экземпляра в другом потоке без средств синхронизации достигается присваиванием:

void MyThread::domSomething()
{
    QString & stringFromOtherThread = ...;
    stringFromOtherThread = "abc";
}

Я всё же рекомендую вам и другим заинтересованым в теме почитать ссылки. А ещё лучше - код. Можете хоть юнит-тест написать, чтобы убедиться.

Dendy ★★★★★
()
Ответ на: комментарий от anonymous

Но это всё значит геморрой вместо простоты STL, где запись в вектор (например vector<int>) выполняется в встроенном виде (inline) за 1 команду процессора без вызова функций и без каких бы то ни было дополнительных действий.

В Qt-контейнерах присваивание тоже делается ровно одной атомарной операцией. Проверка при мутабельной операции тоже выполняется одной атомарной операцией, хотя они (мутабельные операции вроде QString::operator[]) настолько редки, что оверхед на их использование по сравнению с другой логикой просто смешной.

В крайнем случае, если затраты на атомарные операции сравнимы со сложностью самой логики, можно делать вместо:

QVector<int> ints;
ints.resize( 100 );
for ( int i = 0; i < 100; ++i )
    ints[i] = i;

так:

QVector<int> ints;
ints.resize( 100 );
int * pints = ints.data();
for ( int i = 0; i < 100; ++i )
    pints[i] = i;

То-есть проверка на мутабельность всего одна - в ints.data();

Dendy ★★★★★
()
Ответ на: комментарий от Dendy

В результате получается, что действия с контейнером типа operator[] даже в однопоточной программе у QString и QVector требуют обязательной проверки счётчиков ссылок при каждом вызове, хотя я этого может и не просил.

std::vector, string, list тогда предпочтительней и проще для однопоточной работы. Для многопоточной работы с ОДНИМ объектом использование мьютекосв обязательно и в Qt и в STL.

Нахрена оно тогда нужно гемор с QString и QVector. Для особых специфических случаев, где без разделяемых данных них не обойтись, я и сам могу легко написать на основе того же vector.

anonymous
()
Ответ на: комментарий от anonymous

Во-первых, проверка на счётчик ссылок будет и в STL, если конечно реализация не предусматривает глубокое копирование при присваивании, что есть полный ахтунг в наше время.

Во-вторых, мутабельные операции не являются узким местом. Пример с тем же QString::operator[] не состоятелен, поскольку подобные операции практически не используются, и как правило затраты на перерасспределение внутренней структуры контейнера, выделение памяти, создание обьектов, которыми заполняется контейнер - на порядки больше, чем жалкая атомарная операция. Более того, лично я написал десятки тысяч строк кода с использованием QString и ни разу не пользовался QString::operator[], поскольку в абсолютном большинстве случаев контейнеры используются для чтения, а не для записи. Прекратите экономить по одиному такту процессора на секунду в то время как бизнес-логика программы сжирает ресурсы CPU как Карлсон варенье. Если очень необходимо, то как экономить на атомарных операциях я привёл пример выше.

При всём желании не найдёте ни одной программы на Qt, тормозящей именно из-за атомарных проверок copy-on-write.

Dendy ★★★★★
()
Ответ на: комментарий от anonymous

> operator [] не редки. У меня этого навалом в программах.

Хотелось бы посмотреть на код. Как правило новички делают типичную ошибку - используют QString::operator[] для чтения, в то время как нужно использовать константную QString::at(), не проверяющую счётчик ссылок вообще.

Dendy ★★★★★
()
Ответ на: комментарий от Dendy

Ну ты ляпнул.

at как раз проверяет, а [] не проверяет у вектора из stl.

The at() function returns a reference to the element in the vector at index loc. The at() function is safer than the [] operator, because it won't let you reference items outside the bounds of the vector.

http://www.cppreference.com/wiki/stl/vector/at

anonymous
()

Мне Qt-шные классы больше нравятся, с ними удобнее работать. Естественно, тянуть Qt только из за них не стоит, но если проект использует Qt, то почему бы и нет.

Legioner ★★★★★
()
Ответ на: комментарий от Dendy

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

anonymous
()
Ответ на: комментарий от tailgunner

Термин thread-safe относится не к классам, а к методам классов. Когда метод гарантирует, что его можно вызывать одновременно для одного и того же экземпляра класса из разных потоков. reentrant - можно вызывать только из одного потока в один момент времени для экземпляра. А потоконебезопастные методы встретить вообще сложно, как правило это те, что пытаются использовать статические переменные, при этом никаких механизмов защиты этой переменной для одновременного использования в методе нет.

Dendy ★★★★★
()
Ответ на: комментарий от Dendy

> Термин thread-safe относится не к классам, а к методам классов

Блин. Вот цитата из документа:

> Many Qt classes are reentrant, but they are not made thread-safe

В ней ясно сказано, что _классы_ не являются thread-safe. Ты хочешь сказать, что анонимный брат исказил цитату, или документация неправа?

reentrant - можно вызывать только из одного потока в один момент времени для экземпляра.

O_O_O_O_O_O

tailgunner ★★★★★
()
Ответ на: комментарий от anonymous

Вообще-то речь шла про QString::operator[] и QString::at(). Как работать с QString инлайновыми функциями для записи без атомарной проверки я уже написал: QString::data().

Dendy ★★★★★
()
Ответ на: комментарий от tailgunner

* reentrant - Describes a function which can be called simultaneously by multiple threads when each invocation of the function references unique data. Calling a reentrant function simultaneously with the same data is not safe, and such invocations should be serialized.

* thread-safe - Describes a function which can be called simultaneously by multiple threads when each invocation references shared data. Calling a thread-safe function simultaneously with the same data is safe, since all access to the shared data are serialized.

anonymous
()
Ответ на: комментарий от tailgunner

Термин thread-safe применяется не к классам, а к методам класса. Если все методы в классе thread-safe - весь класс называется thread-safe для удобства, чтобы не повторяться в каждом методе отдельно. Если не все методы thread-safe - как каждого метода в отдельности делается приписка thread-safe он или нет.

К примеру контейнеры Qt - не thread-safe, а потокобезопастность работы с ними достигается за счёт создания копий и использования reentrant методов у этих копий. Выше я постарался подробно описать как.

Dendy ★★★★★
()
Ответ на: комментарий от Dendy

>потокобезопастность работы с ними достигается за счёт создания копий и использования reentrant методов у этих копий. Выше я постарался подробно описать как.

Ну насмешил. «ПотокобезопасТноть» не достигается за счёт «создания копий и использования reentrant методов у этих копий». Потому что reentrant означает потокобезопасноть для работы с разными данными, а не для одних и тех же. С одним экземпляром класса без мьютексов никакой потокобезопасности не будет.

anonymous
()
Ответ на: комментарий от tailgunner

«First» is reentrant, thread-safe

class A
{
public:
  char First(const string& s) 
  {
    return s[0];
  }
};

«First» is not reentrant, not thread-safe

int Global = 0;
class A
{
public:
  char First(const string& s) 
  {
    ++Global;
    return s[0];
  }
};

«First» is reentrant, but not thread-safe

class A
{
 int Global;
public:
 char First(const string& s) 
 {
   ++Global;
   return s[0];
 }
};

anonymous
()
Ответ на: комментарий от anonymous

Ну насмешил. «ПотокобезопасТноть» не достигается за счёт «создания копий и использования reentrant методов у этих копий». Потому что reentrant означает потокобезопасноть для работы с разными данными, а не для одних и тех же. С одним экземпляром класса без мьютексов никакой потокобезопасности не будет.

Я рад, что развеселил вас, жалко радоваться вам недолго. Обьясняю, вот reentrant функция:

void i_am_a_reentrant_function( void * p );

Она потому и reentrant, что безопастна для вызова из любого количества потоков при условии, что значения p будут разные. Надеюсь с этим вы спорите не будете? Идём дальше, вот та же reentrant функция для, к примеру, QString::replace():

void qstring_replace( QString * this, from, to );

А вот создание копии для строки:

QString & stringFromOtherThread = ...
QString myCopyOfString = stringFromOtherThread;
myCopyOfString.replace( from, to );

Теперь обратите внимание, что указатель &myCopyOfString не совпадает с указателем &stringFromOtherThread. Первый лежит неизвестно где, второй - у нас в стеке. Значит для этих строк можно вызывать reentrant методы в разных потоках.

Dendy ★★★★★
()
Ответ на: комментарий от anonymous

Отличный пример, забыли правда указать, что s - переменная-член класса A.

Dendy ★★★★★
()
Ответ на: комментарий от Dendy

Насмешил ещё и очень. Потокобезопасность у тебя для РАЗНЫХ экземпляров. STL вся такая «потокобезопасная». И любой класс, не изменяющий глобальные данные, тогда «потокобезопасен».

anonymous
()
Ответ на: комментарий от Dendy

Настоящая потокобезопасноть для экземпляра класса обеспечивается мьютексами на всех его функциях, использующих его внутренне состояние.

А писать про «потокобезопасность» для разных экземпляров класса это комедия. Конечно же они потокобезопасны, потому что они разные экземпляры.

anonymous
()
Ответ на: комментарий от Dendy

> Термин thread-safe применяется не к классам, а к методам класса

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

tailgunner ★★★★★
()
Вы не можете добавлять комментарии в эту тему. Тема перемещена в архив.