Warning: file_get_contents(/data/phpspider/zhask/data//catemap/2/ionic-framework/2.json): failed to open stream: No such file or directory in /data/phpspider/zhask/libs/function.php on line 167

Warning: Invalid argument supplied for foreach() in /data/phpspider/zhask/libs/tag.function.php on line 1116

Notice: Undefined index: in /data/phpspider/zhask/libs/function.php on line 180

Warning: array_chunk() expects parameter 1 to be array, null given in /data/phpspider/zhask/libs/function.php on line 181
C++ 如何在不共享单个变量的情况下统计线程池中已完成的作业?_C++_Multithreading_Memory - Fatal编程技术网

C++ 如何在不共享单个变量的情况下统计线程池中已完成的作业?

C++ 如何在不共享单个变量的情况下统计线程池中已完成的作业?,c++,multithreading,memory,C++,Multithreading,Memory,我有一个线程池,它接受作业(函数指针+数据),将作业交给工作线程完成。一些作业被赋予了一个指向完成计数的指针,当完成时,它们会增加完成计数,因此创建这些作业的主线程可以知道这些作业中有多少已经完成 但问题是,12个以上的线程在一个uint32上竞争。我在沿缓存线分离作业及其数据方面做了大量工作,因此这是我想要消除的唯一争用源,但我不确定如何最好地解决这个特定问题 在多个线程之间不共享单个uint32的情况下,收集已完成作业数的最简单方法是什么 (如果主线程在检查此计数时必须刷新其缓存,这没关系,

我有一个线程池,它接受作业(函数指针+数据),将作业交给工作线程完成。一些作业被赋予了一个指向完成计数的指针,当完成时,它们会增加完成计数,因此创建这些作业的主线程可以知道这些作业中有多少已经完成

但问题是,12个以上的线程在一个
uint32
上竞争。我在沿缓存线分离作业及其数据方面做了大量工作,因此这是我想要消除的唯一争用源,但我不确定如何最好地解决这个特定问题

在多个线程之间不共享单个
uint32
的情况下,收集已完成作业数的最简单方法是什么

(如果主线程在检查此计数时必须刷新其缓存,这没关系,我只想避免弄脏工作线程的缓存。此外,工作线程不需要知道计数,它们只会增加计数,而主线程只能读取计数。)

更新:

我目前正在尝试完全不共享一个计数,而是为每个工作线程创建一个计数,主线程可以在检查时将其相加。其想法是,主线程支付主价格(这很好,因为它正在等待“加入”)

这是我的代码,它在10分钟内就完成了

class Promise {
    friend ThreadPool;
public:

    ~Promise() {
        // this function destroys our object's memory underneath us; no members with destructors
        m_pool->_destroyPromise(this);
    }

    void join() {
        while (isDone() == false) {
            if(m_pool->doAJob() == false){
                // we've no jobs to steal, try to spin a little gentler
                std::this_thread::yield();
            }
        }
    }

    void setEndCount(uint32 count) {
        m_endCount = count;
    }

    bool isDone() {
        return m_endCount == getCount();
    }

    uint32 getCount() {
        uint32 count = 0;
        for (uint32 n = 0; n < m_countCount; ++n) {
            count += _getCountRef(n)->load();
        }
        return count;
    }

    uint32 getRemaining() {
        return m_endCount - getCount();
    }

private:
    // ThreadPool creates these as a factory
    Promise(ThreadPool * pool, uint32 countsToKeep, uint32 endCount, uint32 countStride, void * allocatedData)
        : m_pool(pool)
        , m_endCount(endCount)
        , m_countCount(countsToKeep)
        , m_countStride(countStride)
        , m_perThreadCount(allocatedData)
    {};

    // all worker IDs start at 1, not 0, only ThreadPool should use this directly
    std::atomic<uint32> * _getCountRef(uint32 workerID = 0) {
        return (std::atomic<uint32>*)((char*)m_perThreadCount + m_countStride * workerID);
    }

    // data
    uint32 m_endCount;
    uint32 m_countCount; // the count of how many counts we're counting
    uint32 m_countStride;
    ThreadPool * m_pool;
    void * m_perThreadCount; // an atomic count for each worker thread + one volunteer count (for non-worker threads), seperated by cacheline
};
类承诺{
朋友线程池;
公众:
~Promise(){
//此函数会破坏我们下面对象的内存;没有具有析构函数的成员
m_pool->_promise(本);
}
void join(){
while(isDone()==false){
如果(m_pool->doAJob()==false){
//我们没有工作可以偷,试着转得更温和一点
std::this_thread::yield();
}
}
}
无效setEndCount(uint32计数){
m_endCount=计数;
}
布尔伊斯通{
返回m_endCount==getCount();
}
uint32 getCount(){
uint32计数=0;
对于(uint32 n=0;nload();
}
返回计数;
}
uint32 getRemaining(){
返回m_endCount-getCount();
}
私人:
//ThreadPool将这些创建为工厂
承诺(线程池*pool,uint32 countsToKeep,uint32 endCount,uint32 countStride,void*allocatedData)
:m_池(池)
,m_endCount(endCount)
,m_countcountcount(countstokep)
,m_countStride(countStride)
,m_perThreadCount(allocatedData)
{};
//所有工作ID都从1开始,而不是从0开始,只有ThreadPool应该直接使用它
标准::原子*_getCountRef(uint32 workerID=0){
返回(std::atomic*)((char*)m_perThreadCount+m_countStride*workerID);
}
//资料
uint32 m_结束计数;
uint32 m_countcountcount;//我们正在计数的计数
uint32米/秒;
线程池*m_池;
void*m_perThreadCount;//每个工作线程的原子计数+一个自愿者计数(对于非工作线程),由cacheline分隔
};
更新二:


通过测试,它似乎工作得很好。不幸的是,这是一个相当大的结构,64字节*工作线程数(对我来说这是一个KB),但对于我通常使用的作业,速度交易大约是5%+左右。我想这可能暂时有效。

这真的是个问题吗?您多久检查一次这个数字?您可以用一个标志来表示某个特定作业的完成情况,然后添加标志来确定有多少个已经完成。@Shawn-只有当作业规模相对较小时才有问题。如果作业在对图像应用模糊的作业中处理16x16像素,则可能需要5%的执行时间。是否可以每完成10个任务增加一次标志?以粒度换取速度。@AnneQuinn:当您有许多任务占用相同的时间时,您应该将它们分块以减少调度开销。