【问题标题】:Transferring data to/from a callback from/to a worker thread从工作线程向/从回调传输数据
【发布时间】:2015-09-14 08:46:08
【问题描述】:

我当前的应用程序是一个用 C 编写的玩具 Web 服务,旨在复制 http://sprunge.us/ 的行为(通过 http POST 获取数据,将其存储到磁盘,向客户端返回数据的 url - 还提供具有之前应要求存储)。

应用程序的结构使得线程池由工作线程(只是一个带有 void* 参数的函数指针)实例化,并打开一个套接字来监听传入的连接。该程序的主循环包括一个sock = accept(...) 调用,然后是一个pool_add_task(worker_function_parse_http, sock),以使请求能够被快速处理。 parse_http worker 解析传入的请求,然后将另一个任务添加到工作队列以存储数据 (POST) 或提供先前存储的数据 (GET)。

我对这种方法的问题源于使用http-parser 库,该库使用回调设计返回解析数据(我查看的所有http 解析器都使用这种风格)。我遇到的问题是这样的:

我的parse_http 工人:

  1. 缓冲来自接受的套接字的数据(函数的唯一参数,在这个阶段)
  2. 根据其 API 设置一个 http-parser 对象,并设置回调函数,以便它在完成解析 URL 或 BODY 或其他任何内容时调用。 (这些函数是由http-parser lib 定义的固定类型签名,带有指向包含与调用相关的已解析数据的缓冲区的指针,因此我无法传入自己的变量并以这种方式解决问题。这些函数还向 http 解析器返回状态代码,因此我也不能使用返回值。建议从解析器中获取数据以供以后使用的方法是在回调期间将其复制到全局变量中 - 有趣多个线程。)

  3. 对缓冲的套接字数据执行解析器。在这个阶段,解析器在解析缓冲区的不同部分时应该调用它的设置回调。回调提供了与每个回调相关的解析数据(例如,提供给 body_parsed 回调函数的 BODY 段)。

  4. 嗯,这就是问题所在。解析器已执行,但我无权访问已解析的数据。在这里,我将向队列中添加一个新任务,其中包含一个工作函数来存储接收到的正文数据或另一个来处理对先前存储的数据的 GET 请求。这些函数需要同时提供解析的信息(POST 数据或 GET url)以及接受的套接字,以便现在委派的工作可以响应请求并关闭连接。

当然,这个问题的明显解决方案就是不使用这种带有异步实践的线程池模型,但我现在和以后想知道如何最好地解决这个问题。

如何将这些回调中的解析数据返回到工作线程函数。我考虑过让我的on_url_parsedon_body_parsed 完成应用程序的其余工作(存储和检索数据),但当然我不再需要在这些上下文中响应客户端的套接字。

如果需要,我可以在有机会时将源代码发布到项目中。

编辑:事实证明,可以从这个特定的 http-parser 库的回调中访问用户定义的void *,因为回调传递了对具有用户的调用者(解析器对象)的引用- 可定义的数据字段。

【问题讨论】:

    标签: c multithreading sockets callback pthreads


    【解决方案1】:

    一个设计良好的回调接口可以让你给解析器一个void *,当它调用它们时它会传递给每个回调函数。您提供的回调函数知道它指向什么类型的对象(因为您同时提供了数据指针和函数指针),因此它们可以强制转换和正确取消引用它。除其他优点外,您可以提供回调以访问启动解析的函数的局部变量,而不必依赖全局变量。

    如果您使用的解析器库没有这样的功能(并且您不想切换到设计更好的解析器库),那么您可能可以使用线程本地存储而不是全局变量。具体如何执行取决于您的线程库和编译器,或者您可以通过使用线程标识符作为某些全局数据结构(例如哈希表)中线程特定槽的键来自行开发。

    【讨论】:

    • 感谢您的意见。我认为这可能是一个解决方案,但我不确定我是否过于复杂地试图让线程/异步的东西一起玩得很好,所以很高兴从其他人那里听到它。我确实在这个特定解析器的 API 中没有完全记录的功能中找到了解决方案,所以我将首先尝试。
    猜你喜欢
    • 2011-05-12
    • 2012-11-24
    • 2018-01-30
    • 1970-01-01
    • 2019-09-07
    • 2023-03-09
    • 1970-01-01
    • 2017-06-22
    • 1970-01-01
    相关资源
    最近更新 更多