提问人:nreh 提问时间:10/21/2023 更新时间:10/21/2023 访问量:47
dbus-cxx 阻塞主线程,直到断开 DBUS 连接
dbus-cxx Block main thread until DBUS connection is disconnected
问:
我正在使用 dbus-cxx
库在使用 DBUS 的程序之间进行通信。但是,我感到困惑的一个部分是,当服务器应用程序上的连接打开时,如何防止主线程退出。
我的第一次尝试是只使用无限循环,它会循环直到连接不再有效。
double add(double param1, double param2) { return param1 + param2; }
void TestServer::startServer() {
std::shared_ptr<DBus::StandaloneDispatcher> dispatcher = DBus::StandaloneDispatcher::create();
std::shared_ptr<DBus::Connection> conn = dispatcher->create_connection(DBus::BusType::SYSTEM);
if (conn->request_name("dbuscxx.quickstart_0.server", DBUSCXX_NAME_FLAG_REPLACE_EXISTING) !=
DBus::RequestNameResponse::PrimaryOwner)
return;
// create an object that will contain methods that can be called
std::shared_ptr<DBus::Object> object =
conn->create_object("/dbuscxx/quickstart_0", DBus::ThreadForCalling::DispatcherThread);
// add a method that can be called over the dbus
object->create_method<double(double, double)>("dbuscxx.Quickstart", "add", sigc::ptr_fun(add));
while (conn->is_valid()) {
}
SPDLOG_INFO("Connection status {0}", conn->is_valid());
}
但是,我觉得这真的很低效,浪费了CPU资源。我在网上看到的一种选择是在线程中休眠,这样它只会偶尔检查:
while (conn->is_valid()) {
std::this_thread::sleep_for(std::chrono::seconds(1));
}
我阅读的最好方法是使用 thread::join,但是,dbus-cxx 库不允许我访问运行调度程序或连接的线程,因此我无法调用 join
。我也找不到可以打电话等待的东西。conditional_variable
我能够使用以下方法获取我认为是连接的文件描述符(不确定,但我认为它是通往 DBUS 的管道):
conn->unix_fd();
但是我不知道如何使用它来确定集合何时关闭。我可以以某种方式在连接中使用unix_fd吗?
所以我的问题是,在不重写任何 dbus-cxx 文件的情况下,我怎样才能阻止主线程直到连接关闭(以一种不是非常低效的方式)?
(顺便说一句,对于一个服务来说,有一个无限循环和 1 秒的睡眠对于一项服务来说“足够好”吗?还是效率极低?
任何见解将不胜感激,这是我第一次处理多线程和使用复杂的库。:)
答:
好的,我想我有一个解决方案,但我对其他可能更好的答案持开放态度:
int DBus::Connection::unix_fd()
是一个函数,用于返回用于与主 dbus 服务通信的流的文件描述符。
在我们的代码中,我们可以使用 Linux poll(...)
函数,而不是无限 while 循环,该函数将阻塞我们的代码,直到结构中定义了某些指定事件。pollfd
根据 Linux 手册页的功能:poll
字段 events 是一个输入参数,一个位掩码,指定 应用程序对文件描述符感兴趣的事件 FD的。此字段可以指定为零,在这种情况下,唯一 可以在 revents 中返回的事件包括 POLLHUP、POLLERR 和 POLLNVAL(见下文)。
波尔胡普挂断(仅在 revents 中返回;在事件中忽略)。 请注意,当从管道或 流套接字,此事件仅指示对等体 关闭了通道的末端。后续读取 channel 将仅返回 0(文件末尾) 通道中的未完成数据已被消耗。
波勒尔错误条件(仅在 revents 中返回;在 事件)。此位也设置为文件描述符 当读取端具有 已关闭。
波恩瓦尔无效请求:fd 未打开(仅在 revents 中返回; 在事件中被忽略)。
这非常适合我想要的!
所以现在我们的阻塞代码如下所示:
auto fd = conn->unix_fd();
pollfd fds;
fds.fd = fd;
fds.events = 0;
poll(&fds, 1, -1); // this should (hopefully) block the thread until fd is closed
这样做的好处是,在文件描述符关闭或引发错误之前,内核不会将 CPU 周期专用于主线程。这使它成为满足我需求的完美、最有效的解决方案。
希望这对将来的人有所帮助:)
评论