1

我正在使用 OpenGL,我需要手动处理一些非托管对象。(特别是纹理和顶点缓冲区)。

问题是,释放顶点缓冲区的函数绝对必须从主线程(唯一可以创建或销毁此类对象的线程)调用。

我已经为我的托管对象正确实现了 IDisposable 和析构函数。垃圾收集器也正确调用函数。但是这一切都注定要失败,因为垃圾收集器在不同的线程上运行,当 GC 调用释放函数(DeleteBuffers(...)、DeleteTexture(...) 等)时,它会崩溃。

所以我想出了两个想法来解决这个问题:

  1. 将必须释放的对象添加到列表中,然后在主线程中检查该列表。问题:需要我想避免的同步/锁定。也许使用 BlockingCollection<> ?

  2. 不知何故,强制 GC 使用主线程来完成它的工作。

有没有我不知道的方法可以做到这一点?我应该如何正确处理这些对象?我必须自己清理吗?(意思是我停止使用析构函数并总是自己释放对象?)

4

2 回答 2

1

这个想法很简单,你有一个队列(使用 ConcurentQueue 确保线程安全)让你的 finlizer 填充这个队列,你的主循环清空它。

试试这个链接: http ://www.opentk.com/node/101

A user of the Tao Framework implemented this idea with promising results. He wrote wrappers for OpenGL resources and implemented the disposable pattern like this:
private void Dispose(bool manual)
{
    if (!disposed)
    {
        if (manual)
        {
             Gl.glDeleteTextures(1, ref _tid);
             GC.SuppressFinalize(this);
             disposed = true;
        }
        else
        {
            GC.KeepAlive(SimpleOpenGlControl.DisposingQueue);
            SimpleOpenGlControl.DisposingQueue.Enqueue(this);
        }
    }
}

SimpleOpenGlControl.DisposingQueue 是一个包含对 OpenGL 资源的引用的队列。在程序执行期间定期访问它,处理其中包含的数据。注意'else' 子句永远不会被执行,除非你真的忘记释放资源。这是两全其美的:您可以手动释放资源(不会影响性能),但如果您忘记了什么,垃圾收集器仍会在您之后清理。更好的是,实现非常简单!现在,我们只需要了解如何处理多个 OpenGL 上下文。

于 2012-12-05T10:14:07.743 回答
0

你有没有尝试过:

GCSettings.LatencyMode = GCLatencyMode.Batch;

这应该会停止 GC 在其他线程上运行。

MSDN

禁用垃圾收集并发并在批处理调用中回收对象。这是最具侵入性的模式。此模式旨在以牺牲响应性为代价实现最大吞吐量。

于 2012-12-05T10:09:38.257 回答