如何为多线程向量(LFSV)创建适当的内存池?

时间:2016-07-27 05:47:13

标签: c++ multithreading memory memory-management memory-pool

以下是我大学不再教授课程的旧练习(并行处理)。目标是创建和使用存储库来加速无锁分类矢量实现。我自己实现了Memory Bank,目标是留出足够的内存来使用,所以我不必在LFSV中使用new或delete。我相信我需要一个Get()函数来返回内存的地址(不确定如何跟踪未使用的内存)和Store应该释放内存(以某种方式将其标记为未使用)。

在LFSV内部(在我介入之前工作得很好),练习解释说我应该用新的替换和Store替换new和delete(我们想要释放的内存)。如何创建Get(如果这是不正确的)或Store函数执行就像一个合适的内存库?我还将在线获取您可能知道的任何参考或存储库示例,因为我无法找到与存储库和多线程相关的良好资源。

此程序中没有错误,但由于我没有正确管理内存库,因此返回“失败”。

#include <algorithm>//copy, random_shuffle
#include <ctime>    //std::time (NULL) to seed srand
#include <iostream>       // std::cout
#include <atomic>         // std::atomic
#include <thread>         // std::thread
#include <vector>         // std::vector
#include <mutex>          // std::mutex
#include <deque>          // std::deque

class MemoryBank 
{
    std::deque< std::vector<int>* > slots;
public:
    MemoryBank() : slots(10000) 
    {
        for (int i = 0; i<10000; ++i) 
        {
            slots[i] = reinterpret_cast<std::vector<int>*>(new char[sizeof(std::vector<int>)]);
        }
    }
    ~MemoryBank()
    {
        for (unsigned int i = 0; i < slots.size(); ++i)
        {
            delete slots[i];
        }
        slots.clear();
    }
    void * Get() 
    { 
        return &slots; 
    }
    void Store(std::vector<int *> freeMemory)
    {
        return;
    }
};

class LFSV {
    std::atomic< std::vector<int>* > pdata;
    std::mutex wr_mutex;
    MemoryBank mb;

    public:

    LFSV() : mb(), pdata( new (mb.Get()) std::vector<int> ) {}   

    ~LFSV() 
    { 
        mb.~MemoryBank();
    }

    void Insert( int const & v ) {
        std::vector<int> *pdata_new = nullptr, *pdata_old;
        int attempt = 0;
        do {
            ++attempt;
            delete pdata_new;
            pdata_old = pdata;
            pdata_new = new (mb.Get())std::vector<int>( *pdata_old );

            std::vector<int>::iterator b = pdata_new->begin();
            std::vector<int>::iterator e = pdata_new->end();
            if ( b==e || v>=pdata_new->back() ) { pdata_new->push_back( v ); } //first in empty or last element
            else {
                for ( ; b!=e; ++b ) {
                    if ( *b >= v ) {
                        pdata_new->insert( b, v );
                        break;
                    }
                }
            }
//            std::lock_guard< std::mutex > write_lock( wr_mutex );
//            std::cout << "insert " << v << "(attempt " << attempt << ")" << std::endl;
        } while ( !(this->pdata).compare_exchange_weak( pdata_old, pdata_new  ));
        // LEAKing pdata_old since "delete pdata_old;" will cause errors


//        std::lock_guard< std::mutex > write_lock( wr_mutex );
//        std::vector<int> * pdata_current = pdata;
//        std::vector<int>::iterator b = pdata_current->begin();
//        std::vector<int>::iterator e = pdata_current->end();
//        for ( ; b!=e; ++b ) {
//            std::cout << *b << ' ';
//        }
//        std::cout << "Size " << pdata_current->size() << " after inserting " << v << std::endl;
    }

    int const& operator[] ( int pos ) const {
        return (*pdata)[ pos ];
    }
};

LFSV lfsv;

void insert_range( int b, int e ) {
    int * range = new int [e-b];
    for ( int i=b; i<e; ++i ) {
        range[i-b] = i;
    }
    std::srand( static_cast<unsigned int>(std::time (NULL)) );
    std::random_shuffle( range, range+e-b );
    for ( int i=0; i<e-b; ++i ) {
        lfsv.Insert( range[i] );
    }
    delete [] range;
}

int reader( int pos, int how_many_times ) {
    int j = 0;
    for ( int i=1; i<how_many_times; ++i ) {
        j = lfsv[pos];
    }
    return j;
}

std::atomic<bool> doread( true );

void read_position_0() {
    int c = 0;
    while ( doread.load() ) {
        std::this_thread::sleep_for( std::chrono::milliseconds( 10 ) );
        if ( lfsv[0] != -1 ) {
            std::cout << "not -1 on iteration " << c << "\n"; // see main - all element are non-negative, so index 0 should always be -1
        }
        ++c;
    }
}

void test( int num_threads, int num_per_thread )
{
    std::vector<std::thread> threads;
    lfsv.Insert( -1 );
    std::thread reader = std::thread( read_position_0 );

    for (int i=0; i<num_threads; ++i) {
        threads.push_back( std::thread( insert_range, i*num_per_thread, (i+1)*num_per_thread ) );
    }
    for (auto& th : threads) th.join();

    doread.store( false );
    reader.join();

    for (int i=0; i<num_threads*num_per_thread; ++i) { 
        //        std::cout << lfsv[i] << ' '; 
        if ( lfsv[i] != i-1 ) {
            std::cout << "Error\n";
            return;
        }
    }
    std::cout << "All good\n";
}

void test0() { test( 1, 100 ); }
void test1() { test( 2, 100 ); }
void test2() { test( 8, 100 ); }
void test3() { test( 100, 100 ); }

void (*pTests[])() = { 
    test0,test1,test2,test3//,test4,test5,test6,test7
}; 


#include <cstdio>    /* sscanf */
int main( int argc, char ** argv ) {
    if (argc==2) { //use test[ argv[1] ]
        int test = 0;
        std::sscanf(argv[1],"%i",&test);
        try {
            pTests[test]();
        } catch( const char* msg) {
            std::cerr << msg << std::endl;
        }
        return 0;
    }
}

1 个答案:

答案 0 :(得分:0)

reinterpret_cast真的是“我知道我在做什么,相信我”演员。如果可能,编译器会相信你。

然而,在这种情况下,这是完全错误的。 new char[] 会返回vector<int>*