加入收藏 | 设为首页 | 会员中心 | 我要投稿 李大同 (https://www.lidatong.com.cn/)- 科技、建站、经验、云计算、5G、大数据,站长网!
当前位置: 首页 > 百科 > 正文

在Swift中使用libevent和GCD(libdispatch)

发布时间:2020-12-14 04:57:09 所属栏目:百科 来源:网络整理
导读:我正在 Swift 3中创建一个服务器端应用程序.我选择了libevent来实现网络代码,因为它是跨平台的并且不会遇到C10k问题. Libevent实现了它自己的事件循环,但我想保持CFRunLoop和GCD(DispatchQueue.main.after等)的功能,所以我需要以某种方式粘贴它们. 这就是我
我正在 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正在休眠时,libevent将无法处理事件.当应用程序空闲时,降低此超时会显着增加CPU使用率.

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

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

LINUX更新.上面的代码在Linux上不起作用(2016-07-25-a Swift snapshot),RunLoop.current().run存在但有错误.下面是一个有效的Linux版本,用timer和dispatch_main重新实现.它遇到了相同的延迟问题:

let queue = dispatch_get_main_queue()
let timer = dispatch_source_create(DISPATCH_SOURCE_TYPE_TIMER,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中的支持(可能显然)在不同平台上的实现方式不同.从本质上讲,这意味着RunLoop和libevent就跨平台而言,只是实现同样事物的不同方式.我可以看到libevent可能更适合服务器实现的思想背后的想法,因为CFRunLoop并没有随着特定的目标而成长,但就跨平台而言,它们都在同一棵树上咆哮.

也就是说,RunLoop和libevent使用的底层同步原语本质上是私有的实现细节,更重要的是,平台之间可能不同.从源代码来看,看起来RunLoop在Linux上使用epoll,libevent也是如此,但在macOS / iOS /等上,RunLoop将使用Mach端口作为其基本原语,但libevent看起来似乎会使用kqueue.您可以通过足够的努力,创建一个与给定平台的自由源相关联的混合RunLoopSource,但这可能非常脆弱,而且通常是不明智的,原因如下:首先,它将是基于RunLoop的私有实现细节,这些细节不属于公共API,因此可能随时更改,恕不另行通知.其次,假设你没有通过并为swift和libevent支持的每个平台执行此操作,那么你就会破坏它的跨平台性,这是你在第一时间与libevent一起讨论的原因之一.

您可能没有考虑过的另一个选项是在没有RunLoops的情况下单独使用GCD.查看dispatch_main的文档.在服务器应用程序中,(通常)没有什么特别的“主线程”,因此调度到“主队列”应该足够好(如果需要的话).您可以使用调度“来源”来管理您的连接等.我无法亲自谈论调度源如何扩展到C10K / C100K /等.等级,但在我的经验中,它们看起来非常轻巧,低开销.我也怀疑像这样使用GCD可能是在Swift中编写服务器应用程序的最惯用的方式.作为another answer here的一部分,我写了一个基于GCD的TCP echo服务器的小例子.

如果你被绑定并决定在同一个应用程序中同时使用RunLoop和libevent,那么,正如你所猜测的那样,最好将libevent放在自己独立的线程中,但我认为它并不像你想象的那么复杂.您应该可以自由地从libevent回调中调度dispas_async,并且类似地编组来自GCD托管线程的回复,以便相当容易地使用libevent的多线程机制(即通过锁定运行,或者通过将调用编组为libevent作为事件本身).同样,即使您选择使用libevent的循环结构,使用GCD的第三方库也不应成为问题. GCD管理自己的线程池,无法踩到libevent的主循环等.

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

当你拥有这样的架构时,我通常会喜欢使用最高级别抽象来完成工作的方法,并且只有在特定情况需要时才会降低到较低级别的抽象.在这种情况下,这可能意味着使用CFStreams和RunLoops启动,并切换到“裸”GCD或稍后解放,如果你碰壁并且还确定(通过经验测量)它是传输层而不是应用程序层是限制因素.很少有非平凡的应用程序实际上解决了传输层中的C10K问题;事情往往必须首先在应用层“扩展”,至少对于比基本消息传递更复杂的应用程序.

(编辑:李大同)

【声明】本站内容均来自网络,其相关言论仅代表作者个人观点,不代表本站立场。若无意侵犯到您的权利,请及时与联系站长删除相关内容!

    推荐文章
      热点阅读