【问题标题】:Is there a limit to the number of created events?创建事件的数量是否有限制?
【发布时间】:2018-01-15 13:30:19
【问题描述】:

我正在VS2015 上开发一个C++14 Windows DLL,它可以在所有Windows 版本>= XP 上运行。

TL;DR

使用CreateEvent 创建的事件数量是否有限制,当然名称不同?

背景

我正在编写一个线程池类。 类接口很简单:

void AddTask(std::function<void()> task);

任务被添加到任务队列中,等待的工作人员 (vector &lt;thread&gt;) 在可用时激活任务。

要求

在继续流程之前等待(阻塞)一点任务。这意味着,ThreadPool 的某些用户在调用AddTask 之后可能希望等待一段时间(比如 1 秒)让任务结束,然后再继续流程。如果任务还没有完成,他们仍然会继续流程。

问题

ThreadPool 类不能提供Wait 接口。不是它的责任。

解决方案

  1. 任务完成后ThreadPoolSetEvent
  2. ThreadPool 的用户将等待(或不等待。取决于他们的需要)等待事件发出信号。

所以,我将ThreadPool::AddTask 的返回值从void 更改为int,其中int 是唯一的任务ID,它本质上是任务完成时要单选的事件的名称。

问题

我预计任务不会超过 500 个,但恐怕创建数百个事件是不可能的,甚至是不好的做法。

那么有限制吗?还是更好的方法?

【问题讨论】:

  • 如果您确实采用事件方法,那么从 AddTask 返回一个 HANDLE(或一些 RAII 包装的对象)而不是返回一个模棱两可的 int 不是更有意义吗?不多说?这样,您可以立即等待任务,而不是在某些 int 地图中寻找事件..
  • @David 是对的,这更有意义。这样,您可以使用未命名的事件,这样会更有效率。
  • 我实际上已经更改了函数以返回要发出信号的事件名称。 (将)处理任务的句柄(工作人员)在AddTask 范围内未确定,因此我无法返回它。我可以返回事件本身而不是事件名称。
  • @idanshmu 所以你可以或不能返回一个句柄?不清楚。
  • 不能返回 线程 HANDLE。但是,我可以预先分配一个Event,然后可以返回它的HANDLE。但需要ThreadPool 维护任务到事件HANDLE的映射

标签: c++ windows multithreading events threadpool


【解决方案1】:

你问错问题了。幸运的是,您提供了足够的背景来回答您的真正问题。但在我们开始之前:

首先,如果您要问一个进程可以打开或系统可以容纳的最大事件数是多少,您可能做错了非常非常错误的事情。询问进程可以打开的最大文件数或进程可以创建的最大线程数也是如此。

您可以创建 50、100、200、500、1000 ......但它在哪里停止?如果您甚至考虑创建这么多您必须询问限制的问题,那么您就走错了路。

第二,答案取决于太多的实施细节:操作系统版本、安装的 RAM 数量、注册表设置等等。运行的其他程序也会影响该“限制”。

第三,即使您知道限制 - 即使您可以在运行时根据所有相关因素以某种方式计算它 - 它不会让您做任何可以做的事情'现在还没有

假设您发现限制是 L 并且您现在已经完全创建了 L 事件。另一个任务进来了。你做什么?丢掉任务?在不发出事件信号的情况下执行任务?等到少于 L 个事件,然后才创建一个事件并开始执行任务?进程崩溃?

随便CreateEvent 失败时,你决定你可以做同样的事情。所有这一切都是毫无意义的。这又表明你问错了问题。


但也许你正在做的最错误的事情是说“线程池类不能提供wait,因为它不是它的责任,所以让线程池类为线程池的每个任务提供一个事件将在任务结束时发出信号”(释义)。

看起来你在句子的末尾忘记了开头的前提:不是线程池的责任!

如果您想等待任务完成,请让任​​务本身在完成时发出信号。没有理由使线程池复杂化,因为有时有人想要等待任务。发出任务完成的信号是任务的工作:

event evt;                    ///// this
thread_pool.queue([evt] {
    // whatever
    evt.signal();             ///// and this
});
auto reason = wait(evt, 1s);
if (reason == timeout) {
    log("bummer");
}

event 类可以是您想要的任何东西 - Windows 事件、std::promisestd::future 对,或其他任何东西。

这是如此简单明了。

使线程池基础结构复杂化,白白占用宝贵的系统资源,甚至在没有人收听的情况下发出同步原语的信号,只是为了保存上面两个标记的代码行,在少数情况下您实际上想要等待任务是不合理的.

【讨论】:

  • 好点!我不确定我是否同意这个前提;我认为线程池向其客户端提供等待服务是有意义的,这取决于我认为的上下文。但如果它要这样做,它也可以正确地这样做,通过返回某种带有 .Wait() 的任务对象和其他适当的函数。无论哪种方式,都不需要生成不必要的事件对象。
  • @HarryJohnston:线程池提供等待服务是否有意义已经超出了范围。如果是这样,那根本就没有问题。问题假设它没有意义,并且鉴于它没有意义,解决方案是让任务在完成时通知它。我实际上认为这确实没有意义,但是如果您愿意,我们可以在聊天中讨论。
  • 不需要。无论哪种方式,您的答案都是正确的,所以这是一个有争议的问题。
【解决方案2】:

当然有 a 限制(如果没有其他限制;有时系统会耗尽内存)。

实际上,每个进程的限制约为 1600 万。

您可以在此处阅读更多详细信息:https://blogs.technet.microsoft.com/markrussinovich/2009/09/29/pushing-the-limits-of-windows-handles/

【讨论】:

  • 您链接到的系列的部分处理对象的句柄,而不是对象本身。示例DuplicateHandles 一遍又一遍的相同事件。但是事件本身也存储在非分页池中。如果您每次都创建新事件,那么您会更早地达到限制,不是吗?
  • @conio 很好的观察 - 我没有想到这一点。你可能是对的 - 不过应该很容易用一个小示例程序进行测试..
  • 我在运行 32 位 Windows 10 的 512MB RAM 的 VM 上进行了尝试。Sysinternals 的testlimit.exe -h 给了我 16,744,331(或多或少的链接所说的内容),而我的实用程序(它只是调用 @ 987654324@ 在循环中直到调用失败)只得到 5,410,357。使用 768MB 的 testlimit 仍然可以达到 16,744,362,而我的实用程序只能创建 9,618,776 个事件。使用 1024MB 的 testlimit 再次获得相同数量的句柄 (16,744,362),而我的代码获得 13,758,022 个事件。
猜你喜欢
  • 2014-02-18
  • 2015-03-28
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2015-09-19
  • 2010-10-26
相关资源
最近更新 更多