在许多情况下,资源可以由多个线程共享。资源处理程序可以使用原子引用计数来处理这些资源。
假设您在线程A中有一个资源处理程序,例如std::string a
。假设此std::string
在内部使用原子引用计数机制。现在假设您制作了一个卷影副本,例如std::string b=a
,其中b用于主题B.
我刚读过这个问题:reference counted class and multithreading,那里有两个答案。 first answer表示下面的代码段足够了;注意cleanup
没有任何互斥保护:
if(InterlockedDecrement(&mRefCount)==0)
cleanup();
但second answer使用互斥锁包装cleanup
函数:
void decRef() {
lock(_mutex);
if(InterlockedDecrement(&mRefCount)==0) {
cleanup(); //mainly delete some resource
}
}
我的问题是,“第一个答案是否正确?”当在线程A中销毁实例a
时,这种情况怎么样:
if(InterlockedDecrement(&mRefCount)==0)
// here, OS switch to another thread, and mRefCount is changed there
cleanup();
...其中mRefCount
可以在线程B中同时更改(b
。)在线程B发生这样的更改之后,在线程中调用cleanup()
是否仍然安全?A
答案 0 :(得分:3)
我还没有尝试过很详细地回答这个问题,但我怀疑你一般关心的问题不仅仅是那个问题。
在典型的COW字符串 1 中,当引用计数降至0时,您不再需要使用互斥锁(或类似的东西)来保护清理。
原因很简单:至少在通常情况下,您只能通过复制对字符串的一些现有引用来创建对字符串的新引用。当引用计数降为零时, 不再是任何现有引用,因此没有来源可以创建这样的副本。
如果您使用其他而不是COW字符串,则必须查看用法以确定引用计数是否可能从起始值增加例如,您可以使用某种缓存来保存最近使用的值,这些值使用引用计数,因此只要引用了对象,它就会保留在缓存中。如果不存在对它的进一步引用,则从高速缓存中删除它将符合条件。下次需要向缓存添加内容时,您将找到引用计数为0的最旧项目,并将其替换为新项目(如果没有项目的引用计数为0,则您和#39; d根据缓存策略扩展缓存或拒绝缓存新项目。
在这种情况下, 可以找到引用计数为0的任何项目,然后有一个上下文切换,添加对该项目的引用,然后切换回来(如果你没有使用互斥锁保护清理)删除项目,即使它现在具有非零引用计数。
1.虽然它在这里并不特别重要,但标准现在明确禁止std::string
的写时复制实现。
功能