以线程安全的方式返回指针

5
假设我有一个线程安全的物品集合(称之为ThingList),我想添加以下函数。
Thing * ThingList::findByName(string name)
{
  return &item[name]; // or something similar..
}

但是这样做,我已经将线程安全的责任委托给了调用代码,调用代码需要执行类似以下操作:

try 
{
  list.lock(); // NEEDED FOR THREAD SAFETY
  Thing *foo = list.findByName("wibble");
  foo->Bar = 123;
  list.unlock();  
}
catch (...) 
{
  list.unlock();
  throw;
} 

显然,RAII锁定/解锁对象可以简化/删除try/catch/unlock,但调用者仍然容易忘记。
我看了几个替代方案:
- 返回Thing的值,而不是指针 - 除非需要修改Thing,否则可以接受。 - 添加函数ThingList::setItemBar(string name, int value) - 可以接受,但这些 tend to proliferate。 - 返回类似指针的对象,在创建时锁定列表,在销毁时解锁。不确定这是否是好的实践...
处理这个问题的正确方法是什么?
2个回答

4

没有一种“正确的方法”;它取决于您应用程序的需求。

如果可能的话,通过值返回东西,或者返回一个副本,让调用者可以随意处理。

上述方法的变体是返回可修改的副本,然后提供一种方法将修改后的对象原子地合并回列表中。类似这样:

Thing t = myThingList.getThing(key);
t.setFoo(f);
t.setBar(b);
myThingList.merge(t);     // ThingList atomically updates the appropriate element

然而,如果多个线程尝试更新同一对象,则可能会导致问题。

“指针对象”听起来很酷,但我怀疑当某些锁没有在某处释放时,它会导致难以找到的错误。

我会尝试将所有锁定/解锁代码保留在ThingList中,因此ThingList :: set ... 函数可能是我要做的。


“合并”很好,但是有一个隐藏的假设,即Thing对象知道如何在列表中定位自己。你也可以将“key”传递给合并,但是这样就会出现合并已删除项的问题... - Roddy

3

存储并返回boost::shared_ptr s

在访问期间必须进行锁定,但在解锁后是安全的


1
boost::shared_ptr对集合的线程安全性没有任何保护。 - Kristopher Johnson
这取决于你要处理的线程安全问题。访问集合时(读或写),必须进行锁定,但无论如何都必须这样做。通过返回 shared_ptr,您至少可以保证如果在您之后更改了集合,则仍然可以正常工作(假设集合也是 shared_ptr 的集合)。这是我在高度多线程服务器中一直使用的模型。 - pm100
我不知道shared_ptr是线程安全的,但仔细想想,它没有理由不是。我知道微软的COM使用InterlockedIncrement和InterlockedDecrement来实现非常轻量级的线程安全实现。 - Mark Ransom
a: "访问集合(读或写)时必须进行锁定,但无论如何都必须这样做" - 这正是我试图避免的。 b: "假设集合是shared_ptr的集合"。它不是 - 它是Thing对象的集合。 c: boost::shared_ptr并不比普通指针更安全 - 请参见http://www.boost.org/doc/libs/1_42_0/libs/smart_ptr/shared_ptr.htm#ThreadSafety。 - Roddy

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接