gpt4 book ai didi

swift - 在 Swift 中将 libevent 与 GCD (libdispatch) 一起使用

转载 作者:搜寻专家 更新时间:2023-10-31 08:04:37 24 4
gpt4 key购买 nike

我正在 Swift 3 中创建一个服务器端应用程序。我选择了 libevent 来实现网络代码,因为它是跨平台的,并且没有 C10k 问题。 Libevent 实现了它自己的事件循环,但我想保持 CFRunLoop 和 GCD(DispatchQueue.main.after 等)的功能,所以我需要以某种方式将它们粘合起来。

这是我想出的:

var terminated = false

DispatchQueue.main.after(when: DispatchTime.now() + 3) {
print("Dispatch works!")
terminated = true
}

while !terminated {
switch event_base_loop(eventBase, EVLOOP_NONBLOCK) { // libevent
case 1:
break // No events were processed
case 0:
print("DEBUG: Libevent processed one or more events")
default: // -1
print("Unhandled error in network backend")
exit(1)
}
RunLoop.current().run(mode: RunLoopMode.defaultRunLoopMode,
before: Date(timeIntervalSinceNow: 0.01))
}

这有效,但引入了 0.01 秒的延迟。当 RunLoop 处于 sleep 状态时,libevent 将无法处理事件。当应用程序空闲时,降低此超时会显着增加 CPU 使用率。

我也考虑过只使用 libevent,但是项目中的第三方库可以在内部使用 dispatch_async,所以这可能会有问题。

在不同线程中运行 libevent 的循环会使同步更加复杂,这是解决此延迟问题的唯一方法吗?

LINUX 更新。 以上代码在 Linux 上不起作用(2016-07-25-a Swift 快照), RunLoop.current().run存在错误。下面是使用计时器和 dispatch_main 重新实现的工作 Linux 版本.它也有同样的延迟问题:
let queue = dispatch_get_main_queue()
let timer = dispatch_source_create(DISPATCH_SOURCE_TYPE_TIMER, 0, 0, queue)
let interval = 0.01
let block: () -> () = {
guard !terminated else {
print("Quitting")
exit(0)
}
switch server.loop() {
case 1: break // Just idling
case 0: break //print("Libevent: processed event(s)")
default: // -1
print("Unhandled error in network backend")
exit(1)
}
}
block()
let fireTime = dispatch_time(DISPATCH_TIME_NOW, Int64(interval * Double(NSEC_PER_SEC)))
dispatch_source_set_timer(timer, fireTime, UInt64(interval * Double(NSEC_PER_SEC)), UInt64(NSEC_PER_SEC) / 10)
dispatch_source_set_event_handler(timer, block)
dispatch_resume(timer)
dispatch_main()

最佳答案

在 GitHub 上快速搜索 Open Source Swift Foundation 库会发现 CFRunLoop 中的支持在不同的平台上(也许很明显)以不同的方式实现。这实质上意味着 RunLooplibevent ,就跨平台性而言,只是实现同一件事的不同方式。我可以看到 libevent 背后的想法可能更适合服务器实现,因为 CFRunLoop没有以那个特定的目标长大,但就跨平台而言,他们都在同一棵树上咆哮。

也就是说,RunLoop 使用的底层同步原语和 libevent本质上是私有(private)的实现细节,也许更重要的是,平台之间有所不同。从源码上看是RunLoop用途 epoll在 Linux 上,同样如此 libevent ,但在 macOS/iOS/etc 上,RunLoop将使用 Mach 端口作为其基本原语,但 libevent看起来它会使用kqueue .如果您付出足够的努力,您可能会制作出一个混合 RunLoopSourcelibevent 相关联给定平台的源代码,但这可能非常脆弱,并且通常是不明智的,原因如下:首先,它将基于 RunLoop 的私有(private)实现细节。不属于公共(public) API,因此可能随时更改,恕不另行通知。其次,假设您没有对 Swift 和 libevent 支持的每个平台都执行此操作。 ,你会破坏它的跨平台性,这是你声明的使用 libevent 的原因之一。首先。

您可能没有考虑过的另一种选择是单独使用 GCD,而不使用 RunLoops .查看 dispatch_main 的文档.在服务器应用程序中,“主线程”(通常)没有什么特别之处,因此分派(dispatch)到“主队列”应该足够好(如果需要的话)。您可以使用调度“源”来管理您的连接等。我个人无法说明调度源如何扩展到 C10K/C100K/等。水平,但根据我的经验,它们似乎非常轻巧且开销低。我也怀疑像这样使用 GCD 可能是在 Swift 中编写服务器应用程序的最惯用的方式。作为 another answer here 的一部分,我写了一个基于 GCD 的 TCP 回显服务器的小例子。 .

如果您被约束并决心同时使用 RunLooplibevent在同一个应用程序中,如您所料,最好给出 libevent它有自己的独立线程,但我认为它没有你想象的那么复杂。您应该可以 dispatch_async来自 libevent自由回调,类似地将 GCD 托管线程的回复编码(marshal)至 libevent使用 libevent的多线程机制相当容易(即通过锁定运行,或者通过将您的调用编码到 libevent 作为事件本身。)同样,即使您选择使用 libevent 的第三方库,使用 GCD 的第三方库也不应该成为问题循环结构。 GCD 管理自己的线程池,无法踩到 libevent的主循环等

您还可以考虑构建您的应用程序,以便您使用的并发和连接处理库无关紧要。然后你可以换掉libevent 、GCD、CFStreams 等(或混合搭配),具体取决于对给定情况或部署最有效的方法。选择并发方法很重要,但理想情况下,您不会将自己与它结合得如此紧密,以至于在情况需要时您无法切换。

当您拥有这样的架构时,我通常喜欢使用最高级别的抽象来完成工作的方法,并且仅在特定情况需要时才使用较低级别的抽象。在这种情况下,这可能意味着使用 CFStreamsRunLoops开始,然后切换到“裸”GCD 或 libevent稍后,如果您碰壁并确定(通过经验测量)是传输层而不是应用层是限制因素。很少有重要的应用程序真正解决传输层中的 C10K 问题;事情往往必须首先在应用程序层“向外扩展”,至少对于比基本消息传递更复杂的应用程序。

关于swift - 在 Swift 中将 libevent 与 GCD (libdispatch) 一起使用,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/38425707/

24 4 0
Copyright 2021 - 2024 cfsdn All Rights Reserved 蜀ICP备2022000587号
广告合作:1813099741@qq.com 6ren.com