そこで、初めてマルチスレッド プログラミングを試してみたところ、このヒープ破損の問題に遭遇しました。基本的に、プログラムはランダムな時間 (最短 2 秒、最長 200 秒) 実行された後、クラッシュしてヒープ破損エラーを吐き出します。この件について私が読んだことはすべて、エラーを引き起こす原因と実際にエラーを引き起こす原因とはほとんど関係がないため、診断が非常に難しいことを示しています。そういうわけで、私は困惑したままです。
ただし、私はマルチスレッドについて正式に教えられたわけではないので、ほとんどの場合、概念について理解したことに基づいてプログラミングしていました。私のコードは完全に間違っている可能性があります。だから、ここに私がやろうとしていることと、プログラムが現在それを処理しようとしている方法の基本的な要約があります:
背景のいくつかの視差レイヤーを描画する単純なゲームのコードを書いています。これらのレベルは非常に大きい (たとえば 20000x5000 ピクセル) ため、これらのサイズの画像の 3 つのレイヤーを読み込もうとすることは (不可能ではないにしても) 明らかに不可能です。したがって、現在、画像は500x500の画像に分割されており、すぐに表示する必要がある画像のみをメモリに保持するコードがあります。読み込まれた不要な画像はメモリから削除されます。ただし、単一のスレッドでは、これにより、続行する前にイメージがロードされるのを待っている間に、プログラムが大幅にハングアップします。
これが、私にとってマルチスレッドが理にかなっていると思われるところです。実際に必要な時間までに画像が読み込まれている限り、ゲームの滑らかさに影響を与えることなく、プログラムが必要な読み込みを行うようにしたかったのです。だからここに私がそれを整理した方法があります:
1.) 画像の移動先に関するすべてのデータとそれらに関連付けられたデータはすべて 1 つの多次元配列に格納されますが、最初は画像データは読み込まれません。各フレームで、コードは配列の各位置をチェックし、画像を配置する場所がプレーヤーの半径内にあるかどうかをテストします。
2.) そうである場合、このスポットにロードが必要であるというフラグが立てられます。画像をロードする場所へのポインターは、ベクターに push_back() されます。
3.) レベルが開始されると、2 番目のスレッドが開始されます。このスレッドには、最初に前述のベクトルへのポインターが渡されます。
4.) このスレッドは、スレッドが終了したときにのみ終了する無限の While ループ (それ自体は間違っているように聞こえます) に入れられます。このループは、ベクトルに要素があるかどうかを継続的にチェックします。存在する場合は、0 番目の要素を取得し、画像データをそのポインタにロードしてから、ベクトルから要素を .erase() します。
それはそれがどのように機能するかのかなりの要約です。私の無知な仮定は、2 つのスレッドがある時点で衝突し、同じスペースで一度に書き込みと削除を試みているということです。私がこれに慣れていないことを考えると、この方法は恥ずかしいほどひどいものだと確信しているので、何を改善すべきかを知りたいと思っています.
編集:リクエストに応じてソースコードを追加:
class ImageLoadQueue
{
private:
ImageHandle* image;
std::string path;
int frameWidth, frameHeight, numOfFrames;
public:
ImageLoadQueue();
ImageLoadQueue(ImageHandle* a, std::string b, int c, int d, int e=1) { setData(a,b,c,d,e); }
void setData(ImageHandle* a, std::string b, int c, int d, int e=1)
{
image = a;
path = b;
frameWidth = c;
frameHeight = d;
numOfFrames = e;
}
void loadThisImage() { image->loadImage(path, frameWidth, frameHeight, numOfFrames, numOfFrames); }
};
class ImageLoadThread : public sf::Thread
{
private:
std::vector<ImageLoadQueue*>* images;
public:
ImageLoadThread() { };
ImageLoadThread(std::vector<ImageLoadQueue*>* a) { linkVector(a); }
void linkVector(std::vector<ImageLoadQueue*>* a) { images = a; }
virtual void Run()
{
while (1==1)
{
if (!images->empty())
{
(*images)[0]->loadThisImage();
images->erase(images->begin());
}
}
}
};
class LevelArt
{
private:
int levelWidth, levelHeight, startX, startY, numOfLayers;
float widthScale, heightScale, widthOfSegs, heightOfSegs;
float* parallaxFactor;
ImageHandle** levelImages;
int** frame;
int** numOfFrames;
bool* tileLayer;
bool** isLoaded;
Animation** animData;
std::string** imagePath;
std::vector<ImageLoadQueue*> imageQueue;
ImageLoadThread imageThread;
public:
LevelArt(void);
LevelArt(std::string);
~LevelArt(void);
void loadData(std::string);
void drawLevel(sf::RenderWindow*, float, float);
void scaleLevel(float, float);
void forceDraw(sf::RenderWindow*);
void wipeLevel();
void initialLoad();
int getLevelWidth() { return levelWidth; }
int getLevelHeight() { return levelHeight; }
int getTotalWidth() { return widthOfSegs*levelWidth; }
int getTotalHeight() { return heightOfSegs*levelHeight; }
int getStartX() { return startX; }
int getStartY() { return startY; }
};
これは、このヘッダーにある関連するスレッド コードのほとんどです。levelArt.cpp ファイル内には、格納されているすべての levelArt データを反復処理するための 3 つの入れ子になった for ループが存在し、それらが表示されるプレーヤーの近くに存在するかどうかをテストします。
imageQueue.push_back(new ImageLoadQueue(&levelImages[i][(j*levelWidth)+k], imagePath[i][(j*levelWidth)+k], widthOfSegs, heightOfSegs, numOfFrames[i][(j*levelWidth)+k]));
i、j、k は for ループ イテレータです。