在我的多线程图形应用程序中,我拥有某些资产,例如图像,模型,声音文件等。其中一些是从磁盘上的文件加载的。当这些文件更改时,我想自动重新加载它们并更新可能在整个应用程序中使用的相应资产。一个类似的用例是LOD。当模型距离相机较远时,我想用便宜些,细节较少的版本代替它们。
更换资产后,应用程序的其他部分将在不同的线程中运行,并且可能会读取到这些资产。所以我需要一些锁定。我如何提供适当的锁定来替换资产,同时使应用程序的其他部分尽可能容易地使用资产?
例如,我可以提供资产的抽象基类。它可以包含一个共享的互斥锁。然后,将有一个加载程序类,该类在内部存储资产并返回对它们的引用。
class Asset {
public:
std::shared_mutex access_;
};
class Loader {
public:
template <typename T>
T &load(std::string filename);
private:
std::map<std::pair<std::string, std::type_index>, void*> assets_;
};
template <typename T>
class AssetLoaderTraits;
但是,可能有很多资产,所以让我们尝试更少的互斥锁。例如,加载程序可以保留锁定资产的列表。只有一个互斥锁可以访问列表。而且,我们不再需要资产基础类。
class Loader {
public:
template <typename T>
T &load(std::string filename);
void lock(std::string filename);
bool try_lock(std::string filename, std::chronos::duration trying);
void unlock(std::string filename);
private:
std::map<std::pair<std::string, std::type_index>, void*> assets_;
std::shared_mutex map_access_;
std::map<std::string> locked_assets_;
};
template <typename T>
class AssetLoaderTraits;
但是,我仍然觉得这不是最好的解决方案。如果有成千上万的资产,则通常会对其进行批量处理。因此,资产向量之间存在一个循环,并且每次迭代都需要锁定机制。对于锁定,我还需要记住我要使用的所有资产的文件名。(此外,加载程序持有这些锁也很奇怪。)
std::vector<std::pair<std::string, Image&>> images;
Image &image = loader.load<Image>("/path/to/image.png");
images.push_back(std::make_pair("/path/to/image.png", image));
// ...
for (auto &i : images) {
std::string &filename = i.first;
Image &image = i.second;
loader.lock(filename);
// ...
loader.unlock(filename);
}
有一个更好的方法吗?我觉得我太复杂了,并且监督了一个更简单的解决方案。通常如何解决这种情况?我的目标是为大型资产集合的迭代提供一个简单的界面和良好的性能。
使用互斥锁几乎可以保证您在使用资产时遇到困难。考虑到您开始加载资产的另一个版本,然后显示例程要使用它,但是它已被锁定,因此线程被阻塞,直到被解锁。
您可以改为使用share_ptr,使用者将在资产上保留一个share_ptr,直到不再使用它为止。
加载程序仅加载新数据,加载完成后,会对资产进行原子切换,以便使用者的下一个请求获得新资产。
ps。一些代码审查。
难以阅读的类型声明
std :: map <std :: pair <std :: string,std :: type_index>,void *>
如果您使用使用可以写这样的东西
using AssetId = std::pair<std::string, std::type_index>;
std::map<AssertId, void*>
如果那是你真正的意思。
本文收集自互联网,转载请注明来源。
如有侵权,请联系 [email protected] 删除。
我来说两句