我最近在C++多线程代码中遇到了volatile关键字的奇怪用法.为了抽象编程模式,我们假设有一个控制对象可以被一个生产者和几个消费者线程访问:
class control_t {
pthread_mutex_t control_lock;
pthread_cond_t wake_cond;
bool there_is_work_todo;
control_t volatile* vthis() { return this; }
}
Run Code Online (Sandbox Code Playgroud)
消费者线程执行以下操作(c是指向控件对象的非易失性指针):
...
pthread_mutex_lock(c->control_lock)
while (!c->vthis()->there_is_work_todo) {
pthread_cond_wait(&c->wake_cond, &c->control_lock);
}
...
Run Code Online (Sandbox Code Playgroud)
这里的想法是消费者线程将等待,直到有一些工作要做,生产者通过wake_cond变量发出信号.
我在这里不明白的是为什么控制对象是通过指向"this"的易失性指针访问的,该指针由方法vthis()返回.这是为什么?
我正在使用 mmap 读取大型数据库文件(例如 100GB),该文件的索引保存在主内存中(键偏移对)。
由于默认的 4KB 虚拟内存页面大小,我假设文件系统上的读取调用也将使用 4KB 的块。但是,这对于我的应用程序的访问模式来说效率相当低。因此,我正在研究使用大页面将 I/O 单元的大小从 4KB 透明地增加到 2MB 的可能性。
大页面的典型用途似乎是改善内存分配和 TLB 利用率,但我找不到任何有关其与实际文件 I/O 有何关系的信息。对于mmap,似乎大页面仅支持私有匿名地图。这个假设正确吗?我还尝试查看 libhugetlbfs,但无法找到如何用它读取实际文件。
mmap那么,有没有一种方法可以使用大于 4KB 的 I/O 单元透明地访问文件呢?
下面的代码用于将工作分配给多个线程,唤醒它们,并等待它们完成。在这种情况下,“工作”包括“清理卷”。这个操作到底做什么与这个问题无关——它只是对上下文有帮助。该代码是庞大事务处理系统的一部分。
void bf_tree_cleaner::force_all()
{
for (int i = 0; i < vol_m::MAX_VOLS; i++) {
_requested_volumes[i] = true;
}
// fence here (seq_cst)
wakeup_cleaners();
while (true) {
usleep(10000); // 10 ms
bool remains = false;
for (int vol = 0; vol < vol_m::MAX_VOLS; ++vol) {
// fence here (seq_cst)
if (_requested_volumes[vol]) {
remains = true;
break;
}
}
if (!remains) {
break;
}
}
}
Run Code Online (Sandbox Code Playgroud)
布尔数组中的值_requested_volumes[i]表明线程是否i有工作要做。完成后,工作线程将其设置为 false 并返回睡眠状态。
我遇到的问题是编译器生成一个无限循环,其中变量remains始终为 true,即使数组中的所有值都已设置为 false。这只发生在-O3.
我尝试了两种解决方案来解决这个问题: