简体   繁体   English

在Qt / C ++中从多个线程进行随机函数调用

[英]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.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM