[英]Random function call from multiple threads in Qt/C++
I have a multi-thread QT application that sometimes need a random alphanumeric string from one of its threads (some threads start at application startup, others start or die during lifetime), and I would like to obtain that by calling a function defined in a common header, to avoid code replication. 我有一个多线程QT应用程序,有时需要从其中一个线程中获取随机的字母数字字符串(某些线程在应用程序启动时启动,其他线程在生命周期内启动或终止),我想通过调用在通用头,避免代码复制。
Here there's a code snippet: 这里有一个代码片段:
QString generateRandomAlphanumericString(int length)
{
qsrand(static_cast<uint>(QTime::currentTime().msec())); //bad
QString randomAS = QString();
static const char alphanum[] =
"0123456789"
"ABCDEFGHIJKLMNOPQRSTUVWXYZ"
"abcdefghijklmnopqrstuvwxyz";
for (int i = 0; i < length; ++i)
randomAS[i] = alphanum[qrand() % (sizeof(alphanum) - 1)];
return randomAS;
}
I initially did some mistakes. 我最初犯了一些错误。
At the beginning I called qsrand(static_cast<uint>(QTime::currentTime().msec()));
一开始我叫qsrand(static_cast<uint>(QTime::currentTime().msec()));
in the main function, but I've learned that it should be done per-thread . 在main函数中,但我了解到应该按线程完成。
Then I put the qsrand
call in the function above, but it's not correct . 然后我将qsrand
调用放在上面的函数中,但这是不正确的 。
Please consider that at program startup many threads start "together", so if I initialize the seed with current time in msec the seed is the same among them. 请考虑在程序启动时许多线程“一起”启动,因此如果我以毫秒为单位的当前时间初始化种子,则它们之间的种子是相同的。
Is there a way to change that function accordingly without modify all points in my application where a thread starts its life? 有没有一种方法可以相应地更改该功能,而无需修改我的应用程序中线程开始运行的所有点? Any implementation done in pure C++ (without the use of QT) is fine. 在纯C ++中完成的任何实现(不使用QT)都可以。 Could the new random
C++11 library help in some way to achieve my task? 新的random
C ++ 11库能否以某种方式帮助完成我的任务?
void InitSeedForThread(uint globalSeed, int myThreadIndex)
{
qsrand(globalSeed);
for (int i = 0; i < myThreadIndex; ++i)
qrand();
}
auto GetRandom(int numThreads)
{
for (int i = 0; i < numThreads - 1)
qrand();
return qrand();
}
Given an ordered list of numbers A, B, C, D, E, F, G, H, ...
splits it into n lists. 给定数字A, B, C, D, E, F, G, H, ...
的有序列表,则将其拆分为n个列表。 If n was 4, you would get 如果n为4,您将得到
1. A, E, I, ...
2. B, F, J, ...
3. C, G, K, ...
4. D, H, L, ...
Con: Doing RNG is somewhat expensive, and you're repeating a lot of work. 缺点:进行RNG有点昂贵,并且您正在重复很多工作。 However, since you're doing QT (UI-bound) I'm assuming that performance isn't an issue. 但是,由于您正在执行QT(与UI绑定),因此我认为性能不是问题。
Alternatively, you could do a global random function with a mutex, but that ain't free either. 或者,您可以使用互斥量执行全局随机函数,但这也不是免费的。
I finally found a good solution (thanks everybody who has contributed with comments): 我终于找到了一个很好的解决方案(感谢发表评论的每个人):
enum ThreadData {TD_SEED};
static QThreadStorage<QHash<ThreadData, uint> *> cache;
inline void insertIntoCache(ThreadData data, uint value)
{
if (!cache.hasLocalData())
cache.setLocalData(new QHash<ThreadData, uint>);
cache.localData()->insert(data, value);
}
inline void removeFromCache(ThreadData data)
{
if (cache.hasLocalData())
cache.localData()->remove(data);
}
inline bool hasInCache(ThreadData data)
{
if (!cache.hasLocalData()) return false;
return cache.localData()->contains(data);
}
inline uint getCachedData(ThreadData data)
{
if (cache.hasLocalData() && cache.localData()->contains(data))
return cache.localData()->value(data);
return 0;
}
inline int getThRandom()
{
uint seed = 0;
if (!hasInCache(TD_SEED))
{
seed = QDateTime::currentMSecsSinceEpoch() % 100000000;
#ifdef Q_OS_WIN
seed += GetCurrentThreadId();
#else
seed += QThread::currentThreadId();
#endif
qsrand(static_cast<uint>(seed));
insertIntoCache(TD_SEED, seed);
}
else {
seed = getCachedData(TD_SEED);
}
return qrand();
}
Basically, as suggested by Igor I've made use of QThreadStorage
to store a seed for each thread. 基本上,按照Igor的建议,我利用QThreadStorage
为每个线程存储一个种子。 I've used an hash for future extensions. 我在以后的扩展中使用了哈希。 Then, I've made use of QDateTime::currentMSecsSinceEpoch()
instead of QTime::currentTime().msec()
to have a different number across multiple application starts (if for example the random generated value is stored in a file/db and should be different). 然后,我使用QDateTime::currentMSecsSinceEpoch()
而不是QTime::currentTime().msec()
在多个应用程序启动时使用不同的数字(例如,如果随机生成的值存储在文件/ db中并且应该有所不同)。 Then, I've add an offset, as suggested by UKMonkey , using the thread ID. 然后,按照UKMonkey的建议,使用线程ID添加一个偏移量。
So, my original function will be: 因此,我的原始功能将是:
QString generateRandomAlphanumericString(int length)
{
QString randomAS = QString();
static const char alphanum[] =
"0123456789"
"ABCDEFGHIJKLMNOPQRSTUVWXYZ"
"abcdefghijklmnopqrstuvwxyz";
for (int i = 0; i < length; ++i)
randomAS[i] = alphanum[getThRandom() % (sizeof(alphanum) - 1)];
return randomAS;
}
I've run some tests, producing from different threads thousand of alphanumeric strings, storing them to multiple files and double checked for duplicates among them and between multiple application run. 我进行了一些测试,从不同的线程中生成数千个字母数字字符串,将它们存储到多个文件中,并仔细检查它们之间以及在多个应用程序运行之间是否存在重复。
声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.