同步此事件实现的最佳方法是什么
What's the best way to synchronize this event implementation
以下是我实现C++事件的尝试。
class Event{
typedef std::tr1::function<void( int& )> CallbackFunction;
std::list< CallbackFunction > m_handlers;
template<class M>
void AddHandler(M& thisPtr, void typename (M::*callback)(int&))
{
CallbackFunction bound = std::tr1::bind(callback, &thisPtr, _1);
m_handlers.push_back(bound);
}
void operator()(int& eventArg)
{
iterate over list...
(*iter)(eventArg);
}}
这里的问题是线程安全。如果同时调用AddHandler
和operator()
,事情可能会中断。
同步的最佳方式是什么?使用互斥可能会降低性能。我想知道在这种情况下,boost::信号或C#事件的幕后会发生什么。
首先,在您认为任何实现的可能性不够"快"之前,您需要确定实际的性能要求。你会每秒触发数千次这些事件吗?如果你是,你真的需要一直向处理程序容器添加处理程序吗。
如果出于某种原因,这两个问题的答案实际上都是"是",那么您可能需要调查无锁容器。这意味着要构建自己的容器,而不是使用stl列表。无锁容器将使用原子内部函数(例如windows中的InterlockedCompareExchange)来原子地确定列表的末尾是NULL还是其他。然后,他们将使用类似的内在函数来实际追加到列表中。如果多个线程试图同时添加处理程序,则会出现额外的复杂性。
然而,在一个多核机器和指令重新排序等的世界里,这些方法可能充满危险。我个人使用的事件系统与您描述的没有什么不同,我将其与关键部分一起使用(至少在windows中非常有效),并且我没有遇到性能问题。但另一方面,没有任何东西通过事件系统发送的速度超过大约20Hz
与任何与绩效相关的问题一样,答案总是基于另一个问题的答案;你到底需要在哪里表现?
互斥锁绝对是您想要的。如果每个Event都有自己的互斥,我就不会太担心性能了;原因是,除非在处理事件的过程中添加了大量处理程序,否则互斥体不太可能发生争用并降低速度。
但是,如果在同一对象上有多个线程调用operator()方法,则此互斥可能会出现问题。但如果没有它,您将如何确保以线程安全的方式调用回调?(我注意到您正在传递一个整数引用并返回void,所以我假设这些不是可重入处理程序。)
编辑:你的评论中有一个很好的问题。老实说,当以同步方式使用时,我从来没有考虑过互斥是否有太多开销。所以我做了这个小测试。
#include <stdio.h>
#include <pthread.h>
#define USE_PTHREAD_MUTEX 1
int main(int argc, char * argv[]) {
pthread_mutex_t mutex;
pthread_mutex_init(&mutex, NULL);
long useless_number = 0;
long counter;
for(counter = 0; counter < 100000000; counter++) {
#if USE_PTHREAD_MUTEX
pthread_mutex_lock(&mutex);
#endif
useless_number += rand();
#if USE_PTHREAD_MUTEX
pthread_mutex_unlock(&mutex);
#endif
}
printf("%ldn", useless_number);
}
我在我的系统上运行了这个,并得到了以下运行时。
USE_PTHREAD_MUTEX为0时,平均运行时间为1.2秒。
使用USE_PTHREAD_MUTEX 1,平均运行时间为2.8秒。
因此,要回答你的问题,肯定会有开销。您的里程数可能有所不同。此外,如果多个线程正在竞争对一个资源的访问,那么阻塞必然会花费更多的时间。此外,在纯同步上下文中,访问共享资源的时间可能比等待互斥锁锁定/解锁的时间更长。也就是说,互斥逻辑本身的开销与这些东西相比可能微不足道。
如果list真的是你的类,那么由于它的性质,你不需要每次访问它都锁定。你会锁定一个互斥锁来发布到列表的末尾,当你认为你可能已经到达末尾时也会锁定。
您应该记录类中处理程序的数量,当您即将开始迭代时,您可以愉快地在不锁定的情况下进行迭代,直到达到这个数量。
如果处理程序将被删除,那么就会出现更多的线程争用问题。
- 在C#中处理C++指针而不使用unsafe的最佳方法
- 在C++中,将大的无符号浮点数四舍五入为整数的最佳方法是什么
- 实现无开销push_back的最佳方法是什么
- 在C++中向零方向近似的最佳方法
- 使用不同的CRT将新的C++代码与旧的(二进制)组件隔离开来的最佳方法是什么
- 检测win32服务创建和删除的最佳方法
- 在C++中样板"冷/never_inline"错误处理技术的最佳方法是什么?
- 在 c++ 中对类中的 c 字符串动态数组进行排序的最佳方法是什么?
- 将线程中的数据存储到全局容器的最佳方法?
- 将一系列整数放入类的最佳方法是什么?
- 在派生类中使用基类的私有成员变量的最佳方法
- 在 C++ 中将非指定类型作为参数传递的最佳方法?
- Qt - QVector 和模型视图 - 从列表视图获取自定义类的最佳方法是什么?
- 使用 Git 处理 C++ Visual Studio 2019 解决方案的外部依赖项源代码管理的最佳方法是什么?
- 比较两个节点坐标的最佳方法是什么?
- 在nodejs中使用本机代码的最佳方法是什么?
- 将 pybind11 绑定标记为已弃用的最佳方法
- C++:将向量传递到构造函数以创建成员变量的最佳方法?
- C++中变量混叠的最佳方法
- 读取大文件(>2GB)(文本文件包含以太网数据)并通过不同参数随机访问数据的最佳方法是什么?