2

事前に必要なすべてのメモリを事前に割り当て、完了時に解放する強力なユース ケースがあります。

テストする必要があるこの本当に単純なバッファー プール C++ 実装を作成しましたが、使用しようとしているポインター演算がそれを可能にするかどうかはわかりません。基本的には、次にリリースする部分です。クライアント コードをより複雑にするだけのメモリ ハンドラーに依存するのではなく、このアイデアに何らかのトリックを適用することをお勧めします。

#include <stdio.h>
#include <queue>

#include "utils_mem.h"

using namespace std;

template <class T>
class tbufferpool {
private:
    const int m_initial;
    const int m_size;
    const int m_total;
    T*        m_buffer;
    vector<T*> m_queue;

public:
    // constructor
    tbufferpool(int initial, int size) : m_initial(initial), m_size(size), m_total(initial*size*sizeof(T)) {
        m_buffer = (T*) malloc(m_total);
        T* next_buffer = m_buffer;
        for (int i=0; i < initial; ++i, next_buffer += i*size) {
            m_queue.push_back(next_buffer);
        }
    }

    // get next buffer element from the pool
    T* next() {
        // check for pool overflow
        if (m_queue.empty()) {
            printf("Illegal bufferpool state, our bufferpool has %d buffers only.", m_initial);
            exit(EXIT_FAILURE);
        }
        T* next_buffer = m_queue.back();
        m_queue.pop_back(); 
        return next_buffer;
    }

    // release element, make it available back in the pool
    void release(T* buffer) {
        assert(m_buffer <= buffer && buffer < (buffer + m_total/sizeof(T)));
        m_queue.push_back(buffer);
    }

    void ensure_size(int size) {
        if (size >= m_size) {
            printf("Illegal bufferpool state, maximum buffer size is %d.", m_size);
            exit(EXIT_FAILURE);
        }
    }

    // destructor
    virtual ~tbufferpool() {
        free(m_buffer);
    }
};
4

3 に答える 3

1

STL queue<> コンテナを「ラップ」している理由がわかりません。「バッファ」をキューに入れ、必要に応じてアドレスをプルするだけです。バッファ内の「セグメント」の処理が完了したら、それをキューからポップするだけで、自動的に解放されます。したがって、バッファーへのポインターの代わりに、実際のバッファー クラスを使用するだけです。

車輪の再発明のように思えます。すべてを一度に割り当てる必要があるため、型は構築時に一度に割り当てることができ、必要がない限りメソッドは再割り当てされないvectorため、キューを使用しません。使用方法についてはこちらをご覧ください。vector<>push_back()pop_back()

ただし、基本的には、封筒の裏にある私のアイデアは次のとおりです。

#include <myType.h> // Defines BufferType

const int NUMBUFFERS = 30;

int main()
{
    vector<BufferType> myBuffers(NUMBUFFERS);
    BufferType* segment = &(myBuffers[0]);    // Gets first segment
    myBuffers.pop_back(); // Reduces size by one

    return 0;
}

それがあなたに一般的な考えを与えることを願っています。その方法でベクター内のバッファーを使用するだけで、割り当てまたは割り当て解除が 1 つだけあり、必要に応じてスタックのようなロジックを使用できます。タイプやその他のdequeue標準コンテナも検討する価値があるかもしれませんが、「1つの割り当てまたは割り当て解除のみが必要」である場合は、ベクトルを使用するか、配列へのスマートポインターを使用することもできます。

于 2012-04-05T19:59:40.033 に答える
0

オブジェクトプールを使用して見つけたいくつかのこと:

一度にすべてのオブジェクトを割り当てることについてはわかりません。プールされたすべてのオブジェクトを、独自のプールへのプライベート参照を含む「pooledObject」クラスから派生させるのが好きなので、シンプルでパラメーターのない「リリース」メソッドを許可し、オブジェクトが常にそのプールに解放されることを常に確信しています。独自のプール。静的配列ctorを使用してプール参照を使用してすべてのインスタンスをロードする方法がわかりません-私は常にループでオブジェクトを1つずつ構築してきました。

もう 1 つの便利なプライベート メンバーは、オブジェクトがデプールされたときに設定され、解放されたときにクリアされる「割り当てられた」ブール値です。これにより、プール クラスは、オブジェクトが 2 回解放された場合にすぐに検出して除外することができます。「2 回リリース」エラーは、すぐに検出されない場合、非常に厄介な場合があります。奇妙な動作やクラッシュは数分後に発生し、多くの場合、別のモジュールの別のスレッドで発生します。二重リリースをできるだけ早く検出するのが最善です!

プールのレベルを 1 秒タイマーのステータス バーにダンプすると便利で安心できます。リークが発生した場合は、それが発生しているのを確認できます。多くの場合、数値が驚くほど低下したときに、自分が行っているアクティビティによってリークがどこにあるのかがわかります。Valgrindが必要な人:)

スレッドに関して言えば、プールをスレッド セーフにする必要がある場合は、ブロッキング キューを使用すると便利です。プールが不足すると、オブジェクトを取得しようとするスレッドは、オブジェクトが解放されるまで待つことができ、アプリはクラッシュ/デッドロックする代わりに速度が低下します。また、注意してください。偽の共有。2 つのオブジェクトがキャッシュ ラインを共有しないようにするには、「フィラー」配列データ メンバーを使用する必要がある場合があります。

于 2012-04-05T23:37:02.313 に答える