2

我正在设计一个多线程 c++ 服务器应用程序,它在自己的线程中为客户端提供服务,并且还有各种其他工作线程来执行其他任务。

所有这些线程都将依赖于一个文本配置文件,该文件需要能够在不退出并重新启动进程的情况下重新散列。

我目前正在考虑让每个客户端和工作线程都有自己的配置对象副本,然后在每次重新散列时进行此更新。

我发现的一件事是,将配置传递给我更喜欢的所有其他实用程序函数,并且不认为应该是上述类的一部分似乎变得非常乏味。

除了需要大量同步的痛苦之外,拥有一个全局配置会容易得多。

对如何解决在线程应用程序中具有非恒定全局配置的任何想法持开放态度!

4

5 回答 5

5

我喜欢这样做的方式是始终将配置作为 shared_ptr 携带到 const 对象。每当您需要配置时(请记住一致性;通常最好使用相同的配置处理整个请求,即使该配置在请求处理代码结束时在技术上已经过时),您会得到一个 shared_ptr 。后台工作任务可以在工作周期的方便点重置其 shared_ptr 的值,试图避免保持配置太久(因为它可能会过时)。

如果只有一项任务可以更改配置,则效果很好;它构造了一个全新的配置对象,然后重置它的 shared_ptr,[edit] 它用锁保护它,见下文[/edit]。一旦没有其他任务使用旧配置对象,旧配置对象就会消失。

一个细节:您不能传递指向配置对象部分的指针,除非您确定只要指针持续存在,您就会保留指向该配置对象的 shared_ptr。如果配置包括例如名称到子配置的映射,这可能会很烦人。

虽然共享指针有一些开销,但它可能比保持配置的整个副本同步要少(当然,除非配置很小,如果是这样,我们可能不会进行这种对话)。在大多数应用程序中,配置更改相对较少,因此在任何给定时间您都不太可能拥有两个以上的配置对象。您通常可以安排将 shared_ptr 创建为每个请求一个,因此 shared_ptr 同步是微不足道的。

YMMV,但我发现效果很好。

[编辑] 正如几位评论者所指出的,我应该明确说明锁定要求。配置更新程序保留一个主 shared_ptr,它受到读写锁的保护。它在更新指针时需要持有写锁。它还导出一个接口,该接口将 shared_ptr 返回到当前配置;该接口在持有读锁的同时复制 shared_ptr。由于配置更改很少且 shared_ptr 很小,因此锁争用非常少。 [1]

除了配置任务本身,没有人需要担心锁,因为其他 shared_ptr 不应该被多个任务共享:每个任务都应该有自己的。

[1] 在我写这篇文章的时候,我意识到我一直这样做的方式,包括在主 shared_ptr 上调用 .reset,如果配置的析构函数很慢(如果例如,配置包含大量的 std::string)。最好扩展 reset 的实现(这只是一个带有临时 NULL shared_ptr 的交换),将交换放在锁守卫内并让(临时的)析构函数解锁运行。但是,考虑到配置更改非常罕见(至少在我与之关联的任何服务器中),我怀疑它是否会产生任何明显的差异。

于 2012-09-19T05:49:37.947 回答
1

我不明白这部分:

我发现的一件事是,将配置传递给我更喜欢的所有其他实用程序函数,并且不认为应该是上述类的一部分似乎变得非常乏味。

无论如何,请查看thread-local storage。您可以创建一个线程本地的单例配置对象。

于 2012-09-19T05:03:31.283 回答
1

这可以通过所有线程共享的单个配置对象来处理。这个配置类应该有 2 个部分:

  1. 读取和解析配置文件
  2. 所有线程访问配置数据

配置文件的读取和解析应该只从一个线程处理,最好是主线程。这部分将与其余线程隔离。进行更新后,其余线程访问的配置数据可以通过使用写锁的 pthread_rwlock 进行更新。当线程访问配置数据时,它们应该使用读锁。

如果您不熟悉 rw(读/写)锁,则可能存在多个不会阻塞的同时读锁,除非正在执行写锁。一次只能有一个写锁。因此,在这种情况下,所有线程都可以同时读取配置数据,而不会发生任何锁定争用。只有在 rehash 后主线程更新配置数据时,读取时才会锁定。

于 2012-09-19T05:38:18.920 回答
0

根据定义,如果线程正在访问和改变对象,在多线程应用程序中拥有可变全局对象将需要同步。您可以保留全局配置,但有一些注意事项,如果您在访问配置时添加额外的层,仍然不必担心同步。每个线程都可以有一个指向全局配置的指针,如果每个线程都只是从配置中读取,那么这很好。但是,当您需要更改配置时,请为您正在访问的成员设置一个标志并将其存储在线程本地的某个位置。每次访问配置时,都必须检查此标志,然后返回适当的数据。这在代码设计中具有相当大的优势,并且它迫使您充实配置接口,

于 2012-09-19T05:08:12.053 回答
0

除了已经发布的解决方案之外,另一个好的解决方案是数据库表。数据库可以通过配置提供更好的事务控制,从而提供更好的数据完整性。它可以在对现有配置进行不适当更改的情况下提供保护,因为更改是基于旧的、过时的配置。

是的,它对于 OP 的使用来说速度较慢并且可能会过度杀伤,(特别是如果还没有 DB 的话!),但为了完整起见,应该提到它。

于 2012-09-19T09:58:37.190 回答