C++ 有没有一种方法可以在两个不同的更新源之间进行无锁仲裁?

C++ 有没有一种方法可以在两个不同的更新源之间进行无锁仲裁?,c++,multithreading,thread-safety,locking,lock-free,C++,Multithreading,Thread Safety,Locking,Lock Free,我收到来自两个不同来源(网络监听)的更新,在这两个来源上,我得到了一个类似以下方法的回调: void onUpdate(Update* update) { static MutexT lock; static hash_set<UpdateID> set; ScopedGuard _(lock); //lock here hash_set<UpdateID>::iterator it = set.find(update->id);

我收到来自两个不同来源(网络监听)的更新,在这两个来源上,我得到了一个类似以下方法的回调:

void onUpdate(Update* update)
{
    static MutexT lock;
    static hash_set<UpdateID> set;

    ScopedGuard _(lock); //lock here
    hash_set<UpdateID>::iterator it = set.find(update->id);
    if (it == set.end())
        set.insert(update->id);
    else
        return;

    listener->onUpdate(/*some stuff*/);
}
void onUpdate(更新*更新)
{
静态互斥锁;
静态散列集;
ScopedGuard(锁定);//在此处锁定
hash_set::iterator it=set.find(更新->标识);
if(it==set.end())
设置.插入(更新->标识);
其他的
返回;
listener->onUpdate(/*一些东西*/);
}
由于两个源都向您提供相同的更新,您希望避免通知重复的更新,您希望在两个源之间进行仲裁,使其与最先提供更新的人的最新更新保持一致,并且如果一个源可能不可靠,还需要对错过的更新进行仲裁。问题是,锁定每个更新都很昂贵,如果我确实不想重复
onUpdate
调用,有没有办法绕过这个锁定


(或者至少是一种降低成本的方法?

首先,锁不应该是静态的,它应该是一个成员变量

您可以通过使用读写互斥来提高效率,例如

boost::shared_mutex          mutex_;
std::unordered_set<UpdateID> set_;

void onUpdate(const Update& update)
{
    boost::upgrade_lock<boost::shared_mutex> lock(mutex_);

    auto it = set_.find(update.id);
    if(it == set_.end())
    {
        boost::upgrade_to_unique_lock<boost::shared_mutex> unique_lock(mutex_);
        set.insert(update.id);
    }

    listener->onUpdate(/*some stuff*/);
}
boost::共享互斥体互斥体;
std::无序集集集;
void onUpdate(常量更新和更新)
{
升级锁(互斥锁);
autoit=set.find(update.id);
if(it==set.end())
{
boost::升级到唯一锁唯一锁(互斥锁);
set.insert(update.id);
}
listener->onUpdate(/*一些东西*/);
}

通过两次检查插入条件,可以避免昂贵的锁定。但是更昂贵的是:锁定还是查找。

所以你只是不断地向哈希表添加条目?永远不移除它们?您的哈希表负载是什么样子的?如果发生足够多的冲突,插入/查找性能将降低

如果您的流有序列号,我认为您最好跟踪间隔(缺少序列号),而不是您实际看到的消息。在快速UDP端,当序列号有间隙时,将丢失的消息记录在哈希表中,并对收到的每条消息调用onUpdate()。在慢速TCP端,查看哈希表以查看消息是否填补了空白,是否调用了onUpdate()并将该消息从哈希表中删除,否则什么也不做


也就是说,无锁哈希表是可能的。不确定它们是否无锁,但Microsoft有(和)和TBB有。

这两个源是否提供相同的更新流?您是否考虑过使用非阻塞套接字并在单个线程中执行此操作?我的意思是,如果你有这样一个瓶颈,那么使用多线程有什么好处?@Kerrek,是的,除了一个是udp,这样它可以丢弃数据包,另一个是tcp,并且是slower@NikolaiNFetissov是的,我明白你的意思。它实际上只是一个来自低速服务器的可靠tcp流和一个更快的多播提要。我想我可以将多播回调插入哈希表并回调(无锁),而速度较慢的一个则抓取锁以检查是否需要他。嗯。。。在更新中有序列号吗?据我所知,C++上没有DCLP的可移植实现。@ RANAG请详细说明这是如何取消OP使用的DCLP的?我非常愿意学习。如果不知道“set”是如何实现的,就无法知道DCLP是否会按照预期的方式运行,例如,插入到set中的线程可能在插入操作期间挂起,但对于另一个线程的查找,仍然返回true。你可以阅读更多关于DCLP的危害。这对OP有什么影响?在代码中,要么两个线程都阻塞在
锁(互斥锁)
(如OP的示例中所示),要么在一次竞争中插入两次。在使用
唯一锁(互斥锁)
后,是否要重新检查
设置.find()
,从而使其成为DCLP?除非存在升级的“写”锁,否则锁(互斥锁)将不会阻塞,因此它比OP示例更有效、更可靠(与DCLP不同)。请阅读有关多个读卡器单写器互斥锁的内容。为什么需要重新检查
查找
?如果该项已存在,则插入操作将不会执行任何操作。您是对的。请编辑你的答案(稍作修改),以便我收回我的反对票。对不起,Reader-Writer看起来很合理,虽然我认为升级操作在大多数情况下都会发生,但我也会研究一下,谢谢