在 C/C++ 共享内存中等待和通知
Posted
技术标签:
【中文标题】在 C/C++ 共享内存中等待和通知【英文标题】:wait and notify in C/C++ shared memory 【发布时间】:2011-01-06 07:55:14 【问题描述】:如何在 C/C++ 中的 Java 中等待和通知两个或多个线程之间的共享内存?我使用 pthread 库。
【问题讨论】:
【参考方案1】:如果可用,您可以使用 POSIX 信号量。 pthread 库具有互斥锁,它也可能对您有用。
【讨论】:
【参考方案2】:在您的标题中,您将 C 和 C++ 如此随意地混合为“C/C++”。我希望,您编写的程序不是两者的混合体。
如果您使用的是 C++11,您会发现一个可移植的(因为 C++,所以)比 pthreads 更安全/更易于使用的替代方案(在 POSIX 系统上,它通常在后台使用 pthreads)。
您可以使用std::condition_variable
+ std::mutex
等待/通知。 This example 显示如何:
#include <iostream>
#include <string>
#include <thread>
#include <mutex>
#include <condition_variable>
std::mutex m;
std::condition_variable cv;
std::string data;
bool mainReady = false;
bool workerReader = false;
void worker_thread()
// Wait until main() sends data
std::unique_lock<std::mutex> lk(m);
cv.wait(lk, []return mainReady;);
std::cout << "Worker thread is processing data: " << data << std::endl;
data += " after processing";
// Send data back to main()
std::lock_guard<std::mutex> lk(m);
workerReady = true;
std::cout << "Worker thread signals data processing completed\n";
cv.notify_one();
int main()
std::thread worker(worker_thread);
data = "Example data";
// send data to the worker thread
std::lock_guard<std::mutex> lk(m);
mainReady = true;
std::cout << "main() signals data ready for processing\n";
cv.notify_one();
// wait for the worker
std::unique_lock<std::mutex> lk(m);
cv.wait(lk, []return workerReady;);
std::cout << "Back in main(), data = " << data << '\n';
// wait until worker dies finishes execution
worker.join();
这段代码还强调了 C++ 相对于 C 的其他一些优势:
-
此代码不包含单个原始指针 (which are treacherous)
lambda expressions
其他各种syntactic swagg。
【讨论】:
“我希望,你写的程序不是两者的混合体”混合它们有什么问题? @mFeinstein 在实践中,您经常混合使用它们。但是,当有疑问时,在思考“哦……我应该使用原始指针还是智能指针?”时,您已经在使用 C++(因为 C 没有智能指针),所以您肯定想使用智能指针,除非有一些 API 或其他限制禁止使用它们,或者它们显然是不必要的等等......可以花在解决更难的问题上。【参考方案3】:您需要两个对象,而不是用于等待/通知的 Java 对象:互斥体和条件变量。这些用pthread_mutex_init
和pthread_cond_init
初始化。
如果您需要在 Java 对象上进行同步,请使用 pthread_mutex_lock
和 pthread_mutex_unlock
(请注意,在 C 中您必须自己手动配对)。如果您不需要等待/通知,只需锁定/解锁,那么您不需要条件变量,只需互斥锁。请记住,互斥锁不一定是“递归的”,这意味着如果您已经持有锁,则除非您设置 init 标志以表示您想要该行为,否则您将无法再次使用它。
如果您想拨打java.lang.Object.wait
,请拨打pthread_cond_wait
或pthread_cond_timedwait
。
如果您想拨打java.lang.Object.notify
,请拨打pthread_cond_signal
。
如果您想拨打java.lang.Object.notifyAll
,请拨打pthread_cond_broadcast
。
在 Java 中,等待函数可能会产生虚假唤醒,因此您需要在调用 signal 之前设置一些条件,并在调用 wait 之后检查,并且您需要在循环中调用 pthread_cond_wait
。与在 Java 中一样,互斥锁会在您等待时释放。
与 Java 不同,在 Java 中,除非您持有监视器,否则您不能调用 notify
,您可以实际上在不持有互斥锁的情况下调用 pthread_cond_signal
。但是,它通常不会为您带来任何好处,而且通常是一个非常糟糕的主意(因为通常您想要锁定 - 设置条件 - 信号 - 解锁)。所以最好只是忽略它,像 Java 一样对待它。
实际上并没有更多内容,基本模式与 Java 相同,并非巧合。不过,请务必阅读所有这些功能的文档,因为您想了解和/或避免各种标志和有趣的行为。
在 C++ 中,您可以比仅使用 pthreads API 做得更好。您至少应该将 RAII 应用于互斥锁/解锁,但根据您可以使用的 C++ 库,您最好为 pthreads 函数使用更 C++ 的包装器。
【讨论】:
【参考方案4】:如果您不关心可移植性,Linux 提供了 eventfd,它可以为您提供所需的一切。每个 eventfd 都有一个内部计数器。在默认模式下,如果计数器为零,则从 eventfd 读取阻塞,否则立即返回。写入它会增加内部计数器。
因此,等待调用将只是一个uint64_t buf_a; read(event_fd, &buf_a, sizeof(buf_a));
,其中 buf 必须是一个 8 字节的缓冲区。要通知等待的线程,您可以使用uint64_t buf_b = 1; write(event_fd, &buf_b, sizeof(buf_b));
。
【讨论】:
【参考方案5】:pthread_cond_wait 和 pthread_cond_signal 可用于根据条件进行同步
【讨论】:
【参考方案6】:使用 Condition Variables 是一种方法:在 Linux 下使用 pthread
库时可以使用这些方法(参见链接)。
条件变量是 键入 pthread_cond_t 并与 等待的适当功能 之后,流程继续。
【讨论】:
以上是关于在 C/C++ 共享内存中等待和通知的主要内容,如果未能解决你的问题,请参考以下文章
是否可以在 Windows 中的 PHP 和 C 之间共享内存?
在不同步的情况下读写 SysV 共享内存(使用信号量、C/C++、Linux)