Russian Qt Forum

Qt => Общие вопросы => Тема начата: serg_hd от Май 10, 2010, 14:36



Название: [решено] qrand() внутри потока
Отправлено: serg_hd от Май 10, 2010, 14:36
Если ипользовать метод qrand() (или просто rand()) внутри потока, то он почему-то всё время возвращает одно и то же число:
Код
C++ (Qt)
run()
{
qDebug() << qrand();
}
 
Если за его пределами, то всё нормально, числа разные. В чём причина, может кто-нибудь в курсе?
Сами потоки запускаются в разное время.


Название: Re: [решено] qrand() внутри потока
Отправлено: serg_hd от Май 10, 2010, 14:48
Не совсем понял с чем связано (именно касательно потока), но помогло:
run()
{
 qsrand(QTime(0,0,0).msecsTo(QTime::currentTime()));
 int iRand = (int)qrand();
 qDebug() << iRand;
}


Название: Re: [решено] qrand() внутри потока
Отправлено: Alex Custov от Май 10, 2010, 15:21
доку надо читать

Цитировать
The sequence of random numbers generated is deterministic per thread. For example, if two threads call qsrand(1) and subsequently calls qrand(), the threads will get the same random number sequence.

qsrand/qrand - thread safe функции, они работают per-thread, и хранят свои промежуточные значения в TLS.


Название: Re: [решено] qrand() внутри потока
Отправлено: garryHotDog от Май 10, 2010, 15:41
сталкивался с такой проблемой, что в разных потоках были одинаковые случайные числа....твой код(функция run) имеет туже ошибку что была у меня:
Код
C++ (Qt)
[b]qsrand(QTime(0,0,0).msecsTo(QTime::currentTime()));[/b]
int iRand = (int)qrand();
qDebug() << iRand;
 

ты инициализируешь каждый раз рандомизатор с определенным значеним (кол-во секунд) - ОШИБКА: если 2 потока будут созданы в один и тот же интервал времени у них будут одни и те же случайные числа....
Решение
выполняй qsrand(QTime(0,0,0).msecsTo(QTime::currentTime())); в констукторе главного (основного) класса ТОЛЬКО 1 РАЗ (например в конструкторе), а в потоках (run()) используй просто qrand()....и все будет ок ;D


Название: Re: [решено] qrand() внутри потока
Отправлено: serg_hd от Май 10, 2010, 15:45
... если 2 потока будут созданы в один и тот же интервал времени
дело в том, что не будут  :). Это контролирует другой класс.
Решение
выполняй qsrand(QTime(0,0,0).msecsTo(QTime::currentTime())); в констукторе главного (основного) класса ТОЛЬКО 1 РАЗ (например в конструкторе), а в потоках (run()) используй просто qrand()....и все будет ок ;D
Спасибо, попробовал. Числа одинаковые.
главного (основного) класса
Имеется ввиду ведь класс-наследник QThread'a у которого этот метод run()? Если да, то особой роли этот факт (в конструкторе или нет) играть не будет, т.к. поток запускается сразу же после создания объекта этого класса. Да и числа, как выше написал, будут одинаковые.


Название: Re: [решено] qrand() внутри потока
Отправлено: SimpleSunny от Май 10, 2010, 18:55
... если 2 потока будут созданы в один и тот же интервал времени
дело в том, что не будут  :). Это контролирует другой класс.

Мало ли что может случится потом, поэтому лучше немного обезопасить себя.

qsrand(QDateTime::currentDateTime().toTime_t() + int(this));



Название: Re: [решено] qrand() внутри потока
Отправлено: serg_hd от Май 10, 2010, 19:04
Мало ли что может случится потом, поэтому лучше немного обезопасить себя.

qsrand(QDateTime::currentDateTime().toTime_t() + int(this));
да, в ходе экспериментов это оказался наилучший вариант, блародарствую


Название: Re: [решено] qrand() внутри потока
Отправлено: Alex Custov от Май 10, 2010, 19:13
QDateTime::currentDateTime().toTime_t()

блин, ну напишите вы ::time(0), зачем так сложно-то :)


Название: Re: [решено] qrand() внутри потока
Отправлено: garryHotDog от Май 10, 2010, 23:04
Цитировать
Цитата: garryHotDog от Сегодня в 03:41
главного (основного) класса
Имеется ввиду ведь класс-наследник QThread'a у которого этот метод run()? Если да, то особой роли этот факт (в конструкторе или нет) играть не будет, т.к. поток запускается сразу же после создания объекта этого класса. Да и числа, как выше написал, будут одинаковые.
я имел ввиду выполнить инициализацию qsrand() в каком то одном классе(не потоке), а qrand использовать уже в потоках без повторного использования qsrand()!!!!


Название: Re: [решено] qrand() внутри потока
Отправлено: serg_hd от Май 10, 2010, 23:24
Цитировать
Цитата: garryHotDog от Сегодня в 03:41
главного (основного) класса
Имеется ввиду ведь класс-наследник QThread'a у которого этот метод run()? Если да, то особой роли этот факт (в конструкторе или нет) играть не будет, т.к. поток запускается сразу же после создания объекта этого класса. Да и числа, как выше написал, будут одинаковые.
я имел ввиду выполнить инициализацию qsrand() в каком то одном классе(не потоке), а qrand использовать уже в потоках без повторного использования qsrand()!!!!
и так пробовал, числа одинаковые


Название: Re: [решено] qrand() внутри потока
Отправлено: Alex Custov от Май 11, 2010, 03:26
я имел ввиду выполнить инициализацию qsrand() в каком то одном классе(не потоке), а qrand использовать уже в потоках без повторного использования qsrand()!!!!

qsrand() надо делать для каждого потока, желательно разными значениями. Выше уже всё написано, а в документации и подавно.


Название: Re: [решено] qrand() внутри потока
Отправлено: garryHotDog от Май 11, 2010, 16:12
Цитировать
qsrand() надо делать для каждого потока, желательно разными значениями. Выше уже всё написано, а в документации и подавно.

я так сначала и пробовал: в каждом потоке делал qsrand(), в качестве параметра передавал время в мс....но получалось так что несколько потоков создавалось в один временной интервал и "случайные" числа после qrand() оказывались одинаковыми...попробуй сам!


Название: Re: [решено] qrand() внутри потока
Отправлено: Alex Custov от Май 11, 2010, 16:59
Цитировать
qsrand() надо делать для каждого потока, желательно разными значениями. Выше уже всё написано, а в документации и подавно.

я так сначала и пробовал: в каждом потоке делал qsrand(), в качестве параметра передавал время в мс....но получалось так что несколько потоков создавалось в один временной интервал и "случайные" числа после qrand() оказывались одинаковыми...попробуй сам!

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


Название: Re: [решено] qrand() внутри потока
Отправлено: garryHotDog от Май 11, 2010, 17:02
хмм...что то я все равно не понял почему один раз нельзя!? можно еще раз?


Название: Re: [решено] qrand() внутри потока
Отправлено: Alex Custov от Май 11, 2010, 17:05
хмм...что то я все равно не понял почему один раз нельзя!? можно еще раз?

qrand() и qsrand() работают с одним конкретным, текущим, TLS, сколько раз ещё написать?! ::) Именно поэтому они потокобезопасны.


Название: Re: [решено] qrand() внутри потока
Отправлено: Igors от Май 11, 2010, 19:50
хмм...что то я все равно не понял почему один раз нельзя!? можно еще раз?
Почему "нельзя"? Можно в зависимости от того что Вы хотите получить. По простому говоря, значение генератора случайных чисел копируется в приватные данные каждой нитки. Это хорошо, т.к. гарантирует воспроизводимость: qrand() в одной нитке не зависит от других. Если же не нравится что 2 и более ниток выдают те же числа - надо сделать srand() в самой нитке. Использовать привязки к таймеру - в этом случае получаем невоспроизводимость - могут быть разные результаты при 2 запусках одной программы. Это может быть желательно или нет. Если нужна воспроизводимость, то привязываться к this и.т.п - ненадежно. Лучше напр. так

Код
C++ (Qt)
uint NextSRand( void )
{
static uint theValue = 100;
static QMutex mutex;        // это должно быть static
QMutexLocker lock(&mutex);
return ++theValue;
}
 
// использование в нитке
qsrand(NextSRand());
 


Название: Re: [решено] qrand() внутри потока
Отправлено: BRE от Май 11, 2010, 19:53
Код
C++ (Qt)
uint NextSRand( void )
{
static uint theValue = 100;
QMutex mutex;
QMutexLocker lock(&mutex);
return ++theValue;
}
 

А что защищает локальный мьютекс? :)


Название: Re: [решено] qrand() внутри потока
Отправлено: Igors от Май 11, 2010, 19:55
А что защищает локальный мьютекс? :)
theValue если (вдруг) 2 нитки полезут в NextSRand  :)


Название: Re: [решено] qrand() внутри потока
Отправлено: BRE от Май 11, 2010, 19:58
А что защищает локальный мьютекс? :)
theValue если (вдруг) 2 нитки полезут в NextSRand  :)
Локальный объект создается в стеке, сколько ниток вызовут эту функции, столько объектов и будут создано... и у каждой будет свой мьютекс, ничего не защищающий.  ;)


Название: Re: [решено] qrand() внутри потока
Отправлено: ритт от Май 12, 2010, 00:18
BRE: зачёт :)

static QBasicAtomicInt theIdCounter = Q_BASIC_ATOMIC_INITIALIZER(1);
...
qsrand(theIdCounter.fetchAndAddRelaxed(1));

/* а это зачёт мне :) */


Название: Re: [решено] qrand() внутри потока
Отправлено: Igors от Май 12, 2010, 11:29
Локальный объект создается в стеке, сколько ниток вызовут эту функции, столько объектов и будут создано... и у каждой будет свой мьютекс, ничего не защищающий.  ;)
Верно, mutex также должен быть static, подправился  :)  Хотя если напр. нитки могут создаваться в любом порядке для разных вычислений, предложенный вариант воспроизводимости не обеспечивает. Тогда лучше предвычислить значения для qsrand в главной нитке и передавать их создаваемым ниткам в конструкторах.


Название: Re: [решено] qrand() внутри потока
Отправлено: BRE от Май 12, 2010, 11:52
Хотя если напр. нитки могут создаваться в любом порядке для разных вычислений...
Даже больше. При запуске сразу нескольких ниток, многие операционные системы не гарантируют порядок их запуска. Абсолютно спокойно первой может получить управление нитка запущенная второй по очереди... Соответственно можем получить очень трудно обнаруживаемую непонятку.  :)