似乎有些消息来源建议使用curl_multi_remove_handle
来“使”卷发句柄失效,并使curl_multi_wait
尽早返回。这似乎不在线程安全保证范围之内(如果是从另一个线程执行的话),还是我错了(线程安全保证基本上只是重入保证)?
建议的信号curl_multi_wait
提前返回的方式是什么?它真的需要通过超时来完成吗?(在Linux下,我将使用epoll设置中的event fd来有效地使“等待这些套接字或此事件fd或给定的超时”)。我似乎可以使用自定义的curl_waitfd
结构,但这需要为虚拟套接字设置特定于平台的设置。
发布于 2019-05-15 22:20:13
如果的句柄运行在线程A中,则不能从线程B调用curl_multi_remove_handle
,这只会导致眼泪和痛苦。
您可以选择,例如:
curl_multi_wait()
的超时时间足够短,因此您不需要中止它curl_multi_poll()
在我第一次写了这个答案之后,我们在libcurl中引入了民意测验。这个函数非常类似于curl_multi_wait
,但也允许它先发制人地使用唤醒返回,从而为应用程序提供了更多的替代方法。
发布于 2021-11-16 05:33:48
不幸的是,curl_multi
并不是,现在人们所认为的“线程安全”。是的,您可以在两个不同的线程中使用CURLM
句柄,只要它们不同时访问它。但是,对于C或C++中的几乎任何数据结构,这都是正确的。
因此,如果有一个线程使用curl_multi_wait()
运行事件循环,则不能使用第二个线程通过curl_multi_add_handle()
添加新作业或通过curl_multi_remove_handle()
删除作业。嗯,它在大多数情况下都能工作,但特别是在高负载期间,由于并发访问libcurl的内部数据结构,您将开始获取数据损坏和分段错误。
有两种方法可以解决这个问题,但都需要编写一些代码:
curl_multi_poll()
接口,它(与curl_multi_wait()
不同)可以通过curl_multi_wakeup()
进行外部中断。是的,curl_multi_wakeup()
是CURLM
句柄上的唯一函数,可以安全地从另一个线程(甚至多个线程)并发调用。要向事件循环添加新请求或从事件循环中删除请求,您需要一些请求队列和一个互斥锁,以确保对该队列的访问。然后,要添加一个新工作,您可以这样做:- (thread 1 is running `curl_multi_poll()` in an endless loop)
- thread 2 acquires said mutex
- thread 2 posts an "add easy handle request" into the request queue
- thread 2 releases said mutex again
- thread 2 calls `curl_multi_wakeup()`
- thread 1 acquires the mutex after `curl_multi_poll()` returns
- thread 1 then processes the "add easy handle request" in the job list and performs `curl_multi_add_handle()`
- thread 1 then releases the mutex again
- thread 1 does all other necessary work (in particular call `curl_multi_perform()` and pass finished transfers to the application etc.)
- thread 1 calls `curl_multi_poll()` again
要删除作业,您可以使用相同的过程,让线程2向请求队列发布“删除轻松句柄请求”,而不是“添加轻松句柄请求”,然后让线程1调用curl_multi_remove_handle()
而不是curl_multi_add_handle()
。
在此解决方案中,对CURLM
句柄的所有调用都是从线程1执行的,唯一的例外是curl_multi_wakeup()
,其他线程使用它来向线程1发出在请求队列中等待的新工作的信号。
curl_action()
接口,您必须为libcurl提供两个回调,其中libcurl报告要查看的文件描述符和应用程序的超时。然后,您必须亲自调用epoll()
或类似的OS函数,以等待事件循环线程中的活动(或超时)。然后再添加一个互斥锁,以序列化对CURLM
句柄的访问:您的事件循环线程应该在它调用curl_action()
(或CURLM
句柄上的任何其他函数)之前锁定该互斥锁,然后立即解锁它。由于curl_action()
(不像curl_multi_poll()
)不睡觉,所以这个互斥锁只会在短时间内锁定。因此,其他线程也可以轻松地直接锁定互斥对象,并根据需要调用curl_multi_add_handle()
或curl_multi_remove_handle()
。但是,请注意,这些干预的句柄的添加或删除可以修改活动FD集,您可能需要与事件循环线程进行一些同步,以通知它修改后的epoll()
集。第一个解决方案可能更容易实现。您应该能够在Github上为这两个变体找到libcurl包装器,但在任何关键应用程序中使用它们之前,一定要对它们进行深入的测试。
https://stackoverflow.com/questions/56143716
复制相似问题