Russian Qt Forum

Qt => Общие вопросы => Тема начата: Igors от Декабрь 02, 2009, 20:52



Название: Потокобезопасность простой операции
Отправлено: Igors от Декабрь 02, 2009, 20:52
Добрый вечер

Прошу прощения если вопрос глупый, может просто устал переделывая задачу для всех процессоров.
Выполняется очень простая операция.
Код:
theSum.red += сolor.red;
theSum - общий, доступен всем ниткам. сolor свой у каждой нитки (локальная переменная). Члены классов red - оба float

Вопрос: это thread-safe ("потокобезопасно") или нет? (мое мнение "нет").

Спасибо


Название: Re: Потокобезопасность простой операции
Отправлено: niXman от Декабрь 02, 2009, 21:09
Не безопасно!
А тем более что речь идет о нескольких платформах.
Эта операция находится в функции? Покажите ее.


Название: Re: Потокобезопасность простой операции
Отправлено: Igors от Декабрь 02, 2009, 21:24
Не безопасно!
А тем более что речь идет о нескольких платформах.
Эта операция находится в функции? Покажите ее.
Ну "дословно" показывать никакой ясности не внесет, а смысл такой

Цитировать
struct ARGB {
  float alpha, red, green, blue;
};

ARGB theSum;  // глобальная переменная
....

void ThreadFunc( void * )   // рабочая ф-ция нитки
{
  ...
  ARGB color;
  EvalColor(&color);
  ...
  theSum.red += color.red;
  ...
}


Название: Re: Потокобезопасность простой операции
Отправлено: lit-uriy от Декабрь 02, 2009, 22:00
Несильно в этом понимаю, но, а что если написать volatile перед ARGB theSum? Вроде как компилер, должен теперь боятся её изменения "вдруг" и прнимать спецмеры.


Название: Re: Потокобезопасность простой операции
Отправлено: niXman от Декабрь 02, 2009, 23:29
Код
C++ (Qt)
struct ARGB {
 QMutex mutex;
 float alpha, red, green, blue;
};
 
ARGB theSum;  // глобальная переменная
....
 
void ThreadFunc( void * )   // рабочая ф-ция нитки
{
 ...
 ARGB color;
 EvalColor(&color);
 ...
 theSum.mutex.lock();
 theSum.red += color.red;
 theSum.mutex.unlock();
 ...
}
 


Название: Re: Потокобезопасность простой операции
Отправлено: Alex Custov от Декабрь 03, 2009, 00:25
а что если написать volatile перед ARGB theSum?

Это оградит от оптимизации, а не от параллельного доступа.


Название: Re: Потокобезопасность простой операции
Отправлено: lit-uriy от Декабрь 03, 2009, 00:54
Цитировать
а не от параллельного доступа.
почему?


Название: Re: Потокобезопасность простой операции
Отправлено: Alex Custov от Декабрь 03, 2009, 01:35
Цитировать
а не от параллельного доступа.
почему?

потому что volatile - это compile-time вещь, а доступ к переменным - run-time. Ты не можешь в compile-time гарантировать (без локов), что переменная запишется корректно при доступе из разных потоков. Алёна немного писала про это http://alenacpp.blogspot.com/2006/04/volatile.html


Название: Re: Потокобезопасность простой операции
Отправлено: spectre71 от Декабрь 03, 2009, 08:07
Код
C++ (Qt)
...
 

Немножко не так. QMutex неимоверно дорогой для простейших операций.

Код
C++ (Qt)
struct ARGB {
 float alpha, red, green, blue;
 inline void lock    (void) {while (!mutex.testAndSetAcquire(0, 1));}
 inline void unlock  (void) {mutex.testAndSetRelease(1, 0);)
private:
 QAtomicInt mutex;
};
 
ARGB theSum;  
....
 
void ThreadFunc( void * )  
{
 ...
 ARGB color;
 EvalColor(&color);
 ...
 theSum.lock();
 theSum.red += color.red;
 theSum.unlock();
 ...
}
 

Или так

Код
C++ (Qt)
struct ARGB {
 float alpha, red, green, blue;
};
 
struct SimpleLocker {
 inline void lock    (void) {while (!mutex.testAndSetAcquire(0, 1));}
 inline void unlock  (void) {mutex.testAndSetRelease(1, 0);)
private:
 QAtomicInt mutex;
};
 
ARGB theSum;  
SimpleLocker SumMutex;
....
 
void ThreadFunc( void * )  
{
 ...
 ARGB color;
 EvalColor(&color);
 ...
 SumMutex.lock();
 theSum.red += color.red;
 SumMutex.unlock();
 ...
}
 


Название: Re: Потокобезопасность простой операции
Отправлено: BRE от Декабрь 03, 2009, 08:41
Процессор же кушать будет? Не?
Код
C++ (Qt)
while (!mutex.testAndSetAcquire(0, 1));


Название: Re: Потокобезопасность простой операции
Отправлено: spectre71 от Декабрь 03, 2009, 09:02
Процессор же кушать будет? Не?
Код
C++ (Qt)
while (!mutex.testAndSetAcquire(0, 1));


Будет! Для представленной задачи подходит, в типичной ситуации(когда кто-то уже заблокировал, очень редкая в данном случае) крутимся в цикле очень не долго(несколько итераций цикла).

Вообще-то все нормальные реализации Mutex (в QT не нормальный) устроены так:
Попытка заблокировать:
1) Крутимся на атоме N раз но не меньше 1 (N задается в конструкторе мьютекса или в методе lock,  может быть и 0) - очень дешево
Если не заблокировали
2) Дальше устанавливаем сигнал и перходим в спячку - очень дорого
3) Просыпаемся по сигналу - очень дорого

В QT - так:
1) Проверяем атом 1 раз
Если не заблокировали
2) Дальше устанавливаем сигнал и перходим в спячку - очень дорого
3) Просыпаемся по сигналу - очень дорого


Название: Re: Потокобезопасность простой операции
Отправлено: niXman от Декабрь 03, 2009, 09:06
Цитировать
Немножко не так. QMutex неимоверно дорогой для простейших операций.
Ну раз уж на то пошло, то нужно использовать WriteLock. Чтоб для операций чтения блокировка вообще не использовалась :P


Название: Re: Потокобезопасность простой операции
Отправлено: spectre71 от Декабрь 03, 2009, 09:13
Цитировать
Немножко не так. QMutex неимоверно дорогой для простейших операций.
Ну раз уж на то пошло, то нужно использовать WriteLock. Чтоб для операций чтения блокировка вообще не использовалась :P

Только для Int и для замены указаталя :)
А в данном случае float!


Название: Re: Потокобезопасность простой операции
Отправлено: Igors от Декабрь 03, 2009, 12:06
Спасибо за ответы. Как я понял, все считают что начальный текст НЕ потокобезопасен. Бог с ними с  определениями, хотелось бы узнать: чем мне это гроэит? Я завалю нитку? Вылечу? Разумеется я обнуляю theSum перед запуском ниток и читаю ее только после того как все нитки завершены (в нитках только добавляю в нее).

Другими словами, во что выльется неудобоваримое "непотокобезопасно!"?


Название: Re: Потокобезопасность простой операции
Отправлено: BRE от Декабрь 03, 2009, 12:33
Другими словами, во что выльется неудобоваримое "непотокобезопасно!"?
В неправильное значение theSum, только и всего.  :)


Название: Re: Потокобезопасность простой операции
Отправлено: spectre71 от Декабрь 03, 2009, 12:34
Спасибо за ответы. Как я понял, все считают что начальный текст НЕ потокобезопасен. Бог с ними с  определениями, хотелось бы узнать: чем мне это гроэит? Я завалю нитку? Вылечу? Разумеется я обнуляю theSum перед запуском ниток и читаю ее только после того как все нитки завершены (в нитках только добавляю в нее).

Другими словами, во что выльется неудобоваримое "непотокобезопасно!"?

В результате "гонок":
a) можно получить "мусор" в theSum.red
b) можно считать не правильное значение из  theSum.red


Название: Re: Потокобезопасность простой операции
Отправлено: Alp от Декабрь 03, 2009, 12:50
x += y раскрывается в x = x+y

Для двух потоков:
1. Поток0: Вычисляется x + y0 (=x')
2. Поток1: Вычисляется x + y1 (=x'')
3. Поток0: x = x'
4. Поток1: x = x''

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


Название: Re: Потокобезопасность простой операции
Отправлено: Igors от Декабрь 03, 2009, 13:27
x += y раскрывается в x = x+y
Это правильно "по существу" но код в обоих случаях разный (это не макрос который раскрывается)

Происходит следующее

1) theSum.red и color.red принимаются на регистры (может только theSum.red)
2) выполняется сложение, результат в регистре
3) содержимое регистра записывается в theSum.red

Если не принимать мер, это не работает правильно с 2-мя и более нитками, т.к. др. нитка может "вклиниться" между 1 и 3.

Я выбрал такой способ решения (за счет усложнения кода)
Код:
struct ARGB {
  float alpha, red, green, blue;
};

ARGB theSum[MAX_NUM_THREAD];  // глобальная переменная
....

void ThreadFunc( void * data )   // рабочая ф-ция нитки
{
  ...
  ARGB color;
  EvalColor(&color);
  ...
  theSum[data->threadIndex].red += color.red;
  ...
}


Название: Re: Потокобезопасность простой операции
Отправлено: lit-uriy от Декабрь 03, 2009, 16:44
>>x += y раскрывается в x = x+y
как сказал Igors, это просто логическое раскрытие. Это самостоятельный оператор.
Посмотри описание классов Qt API, в некоторых увидишь перегрузку такого оператора "+=" (или подобного, например "&=").