为啥这些静态函数不按预期从另一个线程返回?

Posted

技术标签:

【中文标题】为啥这些静态函数不按预期从另一个线程返回?【英文标题】:Why don't these static functions return as expected from another thread?为什么这些静态函数不按预期从另一个线程返回? 【发布时间】:2019-07-24 17:42:15 【问题描述】:

我正在尝试使用多个线程使一个函数与另一个函数同时运行,但是当新线程正在运行的函数使用静态函数时,由于某种原因它总是返回 0。

我在 Linux 上使用 Boost 进行线程处理,静态函数在不使用线程时完全按预期工作。我很确定这不是数据竞争问题,因为如果我在创建线程后直接加入线程(不给任何其他代码更改任何内容的机会),问题仍然存在。

创建线程的函数:

void WorldIOManager::createWorld(unsigned int seed, std::string worldName, bool isFlat) 
    boost::thread t( [=]()  P_createWorld(seed, worldName, isFlat);  );
    t.join();
    //P_createWorld(seed, worldName, isFlat); // This works perfectly fine

P_createWorld 中使用静态函数的部分(新创建的线程实际运行的函数): m_world->chunks[i]->tiles[y][x] = createBlock(chunkData[i].tiles[y][x].id, chunkData[i].tiles[y][x].pos, m_world->chunks[i]);

m_world 是一个结构,它包含一个 Chunks 数组,其中包含一个二维 Tiles 数组,每个 Tiles 都有与缓存中的纹理相关联的纹理 ID。 createBlock 返回一个指向完全初始化的新切片指针的指针。有问题的静态函数属于静态链接库,定义如下:

namespace GLEngine 
    //This is a way for us to access all our resources, such as
    //Models or textures.
    class ResourceManager
    
    public:
        static GLTexture getTexture(std::string texturePath);

    private:
        static TextureCache _textureCache;
    ;

还有它的实现:

#include "ResourceManager.h"
namespace GLEngine 
    TextureCache ResourceManager::_textureCache;

    GLTexture ResourceManager::getTexture(std::string texturePath) 
       return _textureCache.getTexture(texturePath);
    

预期结果:为每个图块实际分配其正确的纹理 ID

实际结果:每个图块,无论纹理路径如何,都被指定为 0 作为其纹理 ID。

如果您需要更多代码,例如 tile 的构造函数或 createBlock(),我会很乐意添加它,我只是不知道在这种情况下哪些信息是相关的...

所以,正如我之前所说,如果我没有线程,所有这些都可以完美运行,所以我的最后一个问题是:是否存在某种未定义的行为与线程调用的静态函数有关,或者我只是在这里做错了吗?

【问题讨论】:

编译器是否发出任何警告或错误? 不,它的编译方式与我不使用线程时完全相同 那么你的纹理缓存有多“聪明”?它是否需要担心一个线程正在强制加载纹理而另一个线程正在读取另一个纹理的情​​况。也许 2 个线程都在加载相同的纹理 - 其中一个实际上落在缓存中 - 另一个会发生什么?您可能需要一些互斥锁来停止这里的不确定交互? stl 容器在写入时本质上不是线程安全的。 什么是 P_createWorld ? m_world 在哪里定义?当您说每个图块为零时,您的意思是您放置了一个打印语句或一个断点,并且您实际上观察到 createBlock 的输出为 0,或者您通过检查 m_world 推断它?我想说的是,线程可能正在填充另一个 m_world,例如,如果 m_world 是 thread_local,就会出现这种情况。很可能我错了,但我试图给出另一个观点。 OpenGL 上下文具有线程亲和性。我想知道您是否尝试在后台线程上进行 GL 调用?如果是这样,您将需要一些额外的机制,例如第二个 GL 上下文、两者之间的显式状态共享,以及仔细限制 bg 中发生的操作类型。请参阅khronos.org/opengl/wiki/OpenGL_and_multithreading 或在线搜索“opengl 共享上下文”。 【参考方案1】:

正如@fifoforlifo 提到的,OpenGL 上下文具有线程亲和性,事实证明我在我的纹理加载函数中进行了更深入的 GL 调用。我创建了第二个 GL 上下文并打开了上下文共享,然后它开始工作。非常感谢,@fifoforlifo!

【讨论】:

以上是关于为啥这些静态函数不按预期从另一个线程返回?的主要内容,如果未能解决你的问题,请参考以下文章

如何从静态函数返回函数接口?

在 C++ 中,如何从另一个类中获取静态函数

在破坏调用期间从另一个线程未定义的行为调用对象上的方法?

为啥不需要释放静态数组?

在另一个线程上调用非静态类函数

将线程函数声明为静态函数的问题