Qt / C ++中来自多个线程的随机函数调用

时间:2018-01-25 14:47:37

标签: c++ multithreading qt random

我有一个多线程QT应用程序,有时需要一个来自其中一个线程的随机字母数字字符串(一些线程在应用程序启动时启动,其他线程在生命周期内启动或死亡),我想通过调用一个函数来获取它在公共头中定义,以避免代码复制。

这里有一段代码片段:

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;
}

我最初犯了一些错误。

一开始我在主函数中调用qsrand(static_cast<uint>(QTime::currentTime().msec()));,但I've learned应该完成每个线程

然后我将qsrand调用放在上面的函数中,但它是not correct

请注意,在程序启动时,许多线程会一起开始#34;所以如果我用当前时间以毫秒为单位初始化种子,那么种子就是相同的。

有没有办法在不修改我的应用程序中线程开始生命的所有点的情况下相应地更改该函数? 在纯C ++中完成的任何实现(不使用QT)都可以。新的random C ++ 11库能以某种方式帮助我完成任务吗?

2 个答案:

答案 0 :(得分:0)

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();
}

给出一个有序的数字列表A, B, C, D, E, F, G, H, ... 将其拆分为 n 列表。如果 n 为4,您将获得

1. A, E, I, ...
2. B, F, J, ...
3. C, G, K, ...
4. D, H, L, ...

Con:做RNG有点贵,而且你正在重复做很多工作。但是,由于您正在进行QT(UI绑定),我假设性能不是问题。

或者,您可以使用互斥锁执行全局随机函数,但这也不是免费的。

答案 1 :(得分:0)

我终于找到了一个很好的解决方案(感谢所有提出意见的人):

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();
}

基本上,正如Igor所建议的,我已经使用QThreadStorage来存储每个线程的种子。我已经将哈希用于未来的扩展。 然后,我使用QDateTime::currentMSecsSinceEpoch()代替QTime::currentTime().msec()在多个应用程序启动时使用不同的数字(例如,随机生成的值存储在文件/ db中并且应该不同)。 然后,我使用线程ID添加了UKMonkey建议的偏移量。

所以,我原来的功能是:

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;
}

我已经运行了一些测试,从不同的线程生成数千个字母数字字符串,将它们存储到多个文件中,并在它们之间以及多个应用程序运行之间双重检查重复项。