2

我一直在玩 glib,它

  1. 利用引用计数来管理其对象的内存;
  2. 支持多线程。

我无法理解的是他们如何一起玩。即:

  1. glib每个线程中似乎并没有增加在其输入上传递的对象的引用计数,AFAIK(我将它们称为线程共享对象)。这是真的吗?(或者我只是没能找到正确的代码?)除了主线程(负责对它们进行引用计数)之外,不增加共享它们的每个线程的线程共享对象的引用计数是一种常见的做法吗?
  2. 尽管如此,每个线程都会增加对象的引用计数,这些对象是由自身动态创建的。程序员是否应该为避免名称冲突和内存泄漏而在每个线程中不给出相同的变量名称?(例如,在我的图片上,thread2 不应该创建一个名为 output_object 的堆变量,否则它将与 thread1 的同名堆变量发生冲突)?

更新:对(问题 2)的回答是否定的,因为这些变量的可见性范围不相交: 是动态分配的内存(堆),是函数本地的,还是线程中的所有函数都可以访问它,即使没有传递指针作为论据

我的问题的说明:

在此处输入图像描述

4

2 回答 2

2

我认为线程与理解引用计数器的使用无关。重点是所有权和生命周期,线程只是受此影响的一件事。这有点难以解释,希望我会通过示例更清楚地说明这一点。

现在,让我们看一下 main() 创建一个对象并使用该对象启动两个线程的给定示例。问题是,谁拥有创建的对象?简单的答案是 main() 和两个线程共享这个对象,所以这是共享所有权。为了对此建模,您应该在每次调用 pthread_create() 之前递增 refcounter。如果调用失败,您必须再次递减它,否则启动线程有责任在对象完成时执行此操作。然后,当 main() 终止时,它也应该释放所有权,即递减 refcounter。一般规则是,当添加所有者时,增加 refcounter。当所有者使用完该对象时,它会递减 refcounter,最后一个会销毁该对象。

现在,为什么代码不这样做?首先,您可以将第一个线程添加为所有者,然后将 main() 的所有权传递给第二个线程。这将节省一次递增/递减操作。但这仍然不是正在发生的事情。相反,根本没有进行引用计数,简单的原因是它没有被使用。引用计数的目的是在作为对等的不同所有者之间协调动态分配对象的生命周期。不过,这里的对象是由 main() 创建和拥有的,这两个线程不是对等线程,而是 main 的从属线程。由于 main() 是控制线程启动/停止的主控器,因此它不必与它们协调对象的生命周期。

最后,虽然这可能是由于您的代码的示例性,但我认为 main 只是泄漏了引用,依赖于操作系统进行清理。虽然这并不漂亮,但它并不伤人。通常,您可以分配对象一次,然后在某些情况下永远使用它们而无需任何引用计数。这方面的一个例子是应用程序的主窗口,您只需要一次并且在整个运行时都需要它。但是,您不应该重复分配此类对象,因为这样您就会有明显的内存泄漏,并且会随着时间的推移而增加。不过,这两种情况都会被 valgrind 等工具捕获。

关于您的第二个问题,关于您期望的堆变量名称冲突,它不存在。函数局部变量名不能冲突。这不是因为它们被不同的线程使用,而是即使同一个函数被同一个线程调用两次(想想递归!)每次调用函数中的局部变量都是不同的。此外,变量名称是供人类阅读的。编译器完全消除了这些。

于 2013-01-31T21:34:47.513 回答
1

更新:

正如 matthias 下面所说,GObject 不是线程安全的,只有引用计数函数才是。

原创内容:

GObject 应该是线程安全的,但我自己从来没有玩过……</p>

于 2013-01-31T10:16:38.567 回答