从主线程

时间:2017-02-05 12:55:49

标签: c++ multithreading opengl-es opengl-es-3.0

我有一个纹理加载线程,它接收通过并发队列从主线程加载纹理的请求。

纹理加载器请求是一个简单的结构,带有指向将接收纹理的对象的原始指针:

struct TextureLoaderRequest
{
    std::string mFilename;
    ContentViewer *mContentViewer;
};

ContentViewer中包含的实际纹理对象受互斥锁和一些原子布尔值(也包含在ContentViewer中)保护:

std::atomic<bool>               mIsLoaded;
std::atomic<bool>               mIsVisible;
std::mutex                      mImageMutex;

然后纹理访问例程如下:

void ContentViewer::setTexture(ci::gl::TextureRef texture)
{
    std::lock_guard<std::mutex> guard(mImageMutex);
    mImage = texture;
}

ci::gl::TextureRef ContentViewer::getTexture()
{
    std::lock_guard<std::mutex> guard(mImageMutex);
    if (mIsVisible)
    {
        if (mImage != nullptr)
        {
            mIsLoaded = true;
            return mImage;
        }
        mIsLoaded = false;
    }
    return nullptr;
}

纹理加载器可以一次从主线程接收许多纹理加载请求,然后通过队列加载并将纹理分配给纹理加载请求消息中指向的内容查看器。

我遇到的问题是当主线程“删除”内容查看器时,纹理加载线程可能在其队列中有一个未完成的请求,并且当它处理它时,内容查看器已被删除程序崩溃了。

我不确定如何去除位于纹理线程工作队列中的未完成的纹理加载请求。我不能允许主线程等待为内容查看器加载相关纹理,但是,实现这一目标的最佳实践策略是什么?

由于 - 奠定

3 个答案:

答案 0 :(得分:0)

好的,所以imho有两种选择:

  1. 等待特定对象的所有请求在删除之前完成。
  2. 在执行任何已安排的操作之前,检查对象是否仍然存在。
  3. 我对您的应用程序没有足够的了解:如何实现队列以及为什么以及何时安排请求,因此我无法就此提供反馈。

答案 1 :(得分:0)

我发现我需要用std :: mutex保护的向量构建一个取消列表。当主线程想要退出时,只需在向量中添加一个条目并继续。纹理加载器线程额外负担检查每个接收到的纹理请求的列表,但操作不在关键路径上。

我仍然对替代/建议感兴趣。

线程的一个小概述如下:

void textureLoaderThreadFn()
{
    log("texture loader thread started");

    while (!mShouldQuit)
    {
        // Wait for texture loader request
        TextureLoaderRequest *textureLoaderRequest = nullptr;
        mTextureRequests->popBack(&textureLoaderRequest);

        // it is possible popBack didnt modify textureLoaderRequest (eg. when cancelled on exit)
        if (textureLoaderRequest != nullptr)
        {
            std::lock_guard<std::mutex> lk(mCancellationListMutex);

            if (std::find(mCancellationList.begin(), mCancellationList.end(), textureLoaderRequest->mFilename) != mCancellationList.end())
            {
                // Cancelled

                // we must reset the isLoading that was set by the main thread,
                // so that the request to load the texture can get put back if need be
                textureLoaderRequest->mContentViewer->mIsLoading = false;

                // remove from cancellation list
                mCancellationList.erase(std::remove(mCancellationList.begin(), mCancellationList.end(), textureLoaderRequest->mFilename), mCancellationList.end());
            }
            else
            {
                // Not cancelled
                <SERVICE TEXTURE REQUEST>
            }

            // dont need this anymore
            delete textureLoaderRequest;
        }
    }
    log("texture loader thread stopped");

    // Empty the queue    
    int count = 0;
    TextureLoaderRequest *textureLoaderRequest = nullptr;
    while (mTextureRequests->tryPopBack(&textureLoaderRequest))
    {
        if (textureLoaderRequest != nullptr)
            delete textureLoaderRequest;
        count++;
    }
    log("texture loader thread purged " + std::to_string(count) + " outstanding texture load requests");
}

答案 2 :(得分:0)

我建议您在内容查看器中添加状态标志以区分3种状态;预定着色,着色,未安排着色。 主要线程应仅在其状态被安排为着色或未安排着色时删除内容查看器。

纹理工作线程将状态更改为彩色,一旦着色,就将其置为彩色。

状态的更改和状态检查(如果可以删除)应始终使用相同的互斥锁;您可以在内容查看器中将标志设置为私有,并使用两种公共方法1)void change_status(status)和2)bool can_delete()。

这两个函数应该从获取相同的互斥锁开始。 1)在主线程和纹理工作线程中的不同转换中使用,2)在删除内容查看器之前由主线程使用,只有在状态未被着色时才返回true。

在纹理工作线程中,在退出之前,您可能会删除最后一个彩色内容,如果它没有被主线程删除(因为它可能处于被着色的状态)。