在C ++ 11代码中(可能使用OpenMP),我希望在利用CPU内核提供的并行性时处理多个资源(因此使用线程)。我的问题是保护类的“上下文”免于并发,但我不确定如何达到这个目标!它更像是一个设计问题,而不是纯粹的C ++代码问题,因为目前在单个线程上,我的代码运行得非常完美。现在我想让它适应多线程。
假设我的CPU有4个内核和4 * N资源需要处理。我想要获得的是:启动4个线程,每个线程处理N个资源。这样说,它似乎是无并发的,但不幸的是它不是!
我目前的具体案例是将文件中的资源加载到c ++类中,但是你必须将我的问题视为更常见的情况。
根据上下文,我的意思是:存储在变量成员中的c ++类的当前状态。通过资源,我的意思是(在我的具体情况下):一个文件(ascii或二进制不重要)来读取和存储数据到C ++类。资源是异构的(例如声音,纹理,但它们究竟是什么并不是非常重要)。资源加载的顺序对我来说并不重要(以及平衡的CPU费用)。
我当前的代码,在单个线程上,对于单一类型的资源,看起来像以下伪代码:
class Loader { ... };
class Resource { ... };
string filenames[N] = { "file1.txt", "file2.txt", ... "fileN.txt" };
Resource resources[N];
for (i = 0; i < N; ++i)
{
Loader.loadFromFile(filename[i], &resources[i]);
}
对于多线程,使用OpenMP形式,我想写一些类似的东西:
#pragma omp parallel for
for (i = 0; i < N; ++i)
{
Loader.loadFromFile(filename[i], &resources[i]);
}
不幸的是,我很害怕它不会像伪代码中那样容易,因为类Loader中存储的“上下文”使得它不是线程安全的。这是我目前的实施(简化):
template <class T>
class ILoader
{
public:
...
virtual void loadFromFile(std::string const& filename, T &resource) = 0;
};
class Resource1Loader : public ILoader<Resource1>
{
public:
virtual void loadFromFile(std::string const& filename, Resource1 &resource) override
{
m_infile.open(filename, ...);
...
resource.xxx = m_infile.read(...);
}
private:
std::ifstream m_infile; // The context
};
所有类型的资源(Resource1,Resource2 ... ResourceN)都很长。
注意:为什么我实现这样的类?使LoaderManager包含一个加载器列表(==一个哈希表,其中key是文件扩展名,值是加载器)和一个包含Loaders返回的资源列表的ResourceManager。
m_infile是我的“非线程安全上下文”。这是我读取文件的文件描述符。我被封锁了如何保护它。
这是我的解决方案,但我不喜欢它们:
而不是:
#pragma omp parallel for
for (i = 0; i < X; ++i)
{
LoaderR1.loadFromFile(filenameR1[i], &resourcesR1[i]);
}
#pragma omp parallel for
for (i = 0; i < Y; ++i)
{
LoaderR2.loadFromFile(filenameR2[i], &resourcesR2[i]);
}
...
要做到这一点:
#pragma omp parallel for
for (i = 0; i < min(X, Y); ++i)
{
LoaderR1.loadFromFile(filenameR1[i], &resourcesR1[i]);
LoaderR2.loadFromFile(filenameR2[i], &resourcesR2[i]);
}
其中X!= Y且其中:
Resource1 resourcesR1[X]; string filenamesR1[X] = { "fileR1_1.txt", "fileR1_2.txt", ... "fileR1_X.txt" };
Resource2 resourcesR2[Y]; string filenamesR2[Y] = { "fileR2_1.txt", "fileR2_2.txt", ... "fileR2_Y.txt" };
...
ResourceN resourcesRn[Z]; string filenamesRn[Z] = { "fileRn_1.txt", "fileRn_2.txt", ... "fileRn_Z.txt" };
这意味着:每个线程加载N Resource1和N Resource2。虽然互斥锁保护R1免受R1(以及R2对R2)的影响,但它允许将R1和R2加载在一起。
那么如何修改我的课程以达到我的目标呢?注意:我想在运行时加载资源,但在运行时没有“动态”加载,如OpenGL loading resource in separated thread中所述(虽然很有趣,但它不是我正在看的)。
提前致谢!
答案 0 :(得分:0)
没有理由认为m_infile必须是成员变量。只需将加载程序代码更改为:
virtual void loadFromFile(std::string const& filename, Resource1 &resource) override
{
std::ifstream infile.open(filename, ...);
...
resource.xxx = infile.read(...);
}
您还没有清楚地解释为什么您认为ifstream对象需要成为成员变量,但实际上它不太可能是必需的。