如何解决Vala 如何使用多线程处理引用计数?
据我所知,在多线程环境中,引用计数应该通过锁定来执行,以确保所有线程看到相同的内存快照。但是锁定会降低性能。 Vala 是如何解决这个问题的?
解决方法
引用计数主要在 GObject 中处理(对于 GLib.Object
派生类型),它反过来使用 GLib 中的 Atomic Operations。原子是一个棘手的话题。如果您想深入了解细节,最好从 Herb Sutter 几年前的 Atomic Weapons 演讲开始。我建议您观看这些视频,即使您永远不会使用它们(并且 99.9% 的程序员不应该使用它们),因为它会让您更好地了解计算机的实际工作原理。
“原子”这个名字可能有点误导;这不是真正关于 atomicicity,尽管这是其中的一部分。从某种意义上说,这些操作是原子的,即更改要么全部进行,要么根本不进行,这很重要,但更有趣的部分是原子充当障碍,阻止编译器重新跨越障碍的排序操作。赫伯·萨特 (Herb Sutter) 的演讲详细介绍了这一点,我不会在这里重复。
例如,考虑一个简单的不受保护的引用计数器:
typedef struct {
int reference_count = 0;
} Foo;
Foo* foo_create(void) {
Foo* foo = malloc(sizeof(Foo));
foo->reference_count = 1;
}
void ref(Foo* foo) {
++(foo->reference_count);
}
void unref(Foo* foo) {
if (--(foo->reference_count) == 0) {
free(foo);
}
}
我假设您可以看到不加保护的问题,因为我正在写一篇 SO 帖子而不是一本书。
我们感兴趣的特定原子操作是 compare-and-swap (CAS),它基本上提供了安全执行此操作的能力:
bool cas(int* value,int* expected,int desired) {
if (*value == *expected) {
*value = desired;
return true;
} else {
return false;
}
}
使用它,我们将上面的引用计数实现更改为:
typedef struct {
int reference_count = 0;
} Foo;
Foo* foo_create(void) {
Foo* foo = malloc(sizeof(Foo));
/* No atomics needed,we haven't made the value public yet */
foo->reference_count = 1;
}
void ref(Foo* foo) {
int old_refcount;
int new_refcount;
do {
current_refcount = foo->reference_count;
new_refcount = current_refcount + 1;
} while (!cas (&(foo->reference_count),&old_refcount,new_refcount))
}
void unref(Foo* foo) {
int old_refcount;
int new_refcount;
do {
current_refcount = foo->reference_count;
new_refcount = current_refcount - 1;
} while (!cas (&(foo->reference_count),new_refcount));
if (new_refcount == 0) {
free(foo);
} else if (new_recount < 0) {
// Double-free bug,code should not be reached!
}
}
但是锁定会降低性能。
原子也是如此。很多。但也比更高级别的锁要少得多。一方面,如果您使用互斥锁,您所做的基本上是:
- 获取锁。
- 执行操作。
- 解除锁定。
对于原子,我们基本上是在乞求宽恕而不是征求许可:
- 尝试执行操作。
然后我们只看操作是否成功(即,如果 cas()
返回 true)。
操作也小了很多,速度也快了很多;使用互斥体,您可能会获取锁然后读取当前值,递增/递减它,然后释放锁。使用原子,CAS 操作被包裹在单个 CPU 指令中。
CPU 仍然必须通过确保下次任何其他内核(有点过于简化,因为即使在一个内核内也有多个缓存)要求读取数据时处理缓存一致性问题,并提供新数据。换句话说,原子引用计数对性能不利,但比互斥锁要好得多。坦率地说,如果您想要引用计数而不是跟踪垃圾收集,原子几乎是您最不坏的选择。
版权声明:本文内容由互联网用户自发贡献,该文观点与技术仅代表作者本人。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如发现本站有涉嫌侵权/违法违规的内容, 请发送邮件至 dio@foxmail.com 举报,一经查实,本站将立刻删除。