在主线程上启动对话框等待工作线程的结果

Posted

技术标签:

【中文标题】在主线程上启动对话框等待工作线程的结果【英文标题】:Launch dialog on main thread waiting for result of worker thread 【发布时间】:2020-03-20 17:53:06 【问题描述】:

我有一个应用程序可以发送 HTTP 请求并处理收到的响应。主线程被阻塞,直到响应返回,否则我们无法处理数据。要发送这些请求,用户必须经过身份验证。我希望捕获 401 响应,并在返回响应以供我的应用程序处理之前,提示用户进行身份验证。根据成功与否,我想重试发送原始请求并返回 that 响应,或者,如果身份验证失败,则返回原始 401 响应。

我正在使用 C++ REST SDK 发送 HTTP 请求。这些发生在另一个线程(pplx::task)中。我还使用 MFC 模式对话框来提示身份验证。你们中的一些人可能会看到发生的死锁。如果没有,让我再解释一下。

主线程等待 HTTP 请求完成。在该线程中,我捕获了 401 并希望启动一个对话框。为此,我使用boost::signal。该信号将SendMessage 调用到我希望显示对话框的句柄。在消息被 MFC 消息循环处理后,它将启动对话框(在主线程上)。这依赖于 MFC 消息循环,该循环在等待 HTTP 请求时被阻塞。简而言之,主线程已经在等待请求完成,因此它无法运行其消息循环来接收来自SendMessage 的调用。

主线程正在等待工作线程。工作线程需要在主线程上启动一个对话框才能继续。僵局。有没有人有任何聪明的解决方案来解决这个问题?

【问题讨论】:

你肯定想要使用线程,但你需要让你的 UI 线程“呼吸”。如果有的话,禁用除取消按钮之外的所有控件。不要“等待”这些线程以线程为基础完成。相反,让他们通过方法调用通知对话框他们的结果(或者更好的是,在对话框中调用“更新状态”方法以获得每个线程的状态或完成百分比或???)的更多分辨率。 您的描述有点混乱。你有多少对话框?只有一个(用于身份验证)或更多?此外,验证对话框的显示是要采取的第一个操作,还是必须等待某些 HTTP 请求完成?请更详细地描述所需的操作和事件的顺序,最重要的是按正确的顺序。 为简单起见,我们只说有主对话框和身份验证对话框。与主对话框交互会触发 HTTP 请求。我最关心的是身份验证过期并尝试发送请求。在实践中,这应该很少发生。如果是这样,我想打开身份验证对话框并重试请求。流程如下:用户在主对话框中单击 btn。 Btn 触发 HTTP。如果为 200,则显示响应结果。如果是 401,则显示身份验证对话框。如果成功,请重试 HTTP。如果响应是另一个失败或身份验证失败,那么假设程序关闭。 所以你的第一个动作是显示主对话框。问题是,如果尚未执行身份验证,是否允许用户在主对话框中执行任何操作?如果没有,最好先执行身份验证并仅在成功时显示主对话框。 用户可以在没有身份验证的情况下执行有限的操作。大多数情况下,用户在首次启动时会自动登录或提示。所以通常这不是问题。但是,如果应用程序长时间打开(并且没有刷新令牌),则身份验证可能会过期,这意味着他们的请求将失败。此时他们必须手动提示登录。如果它碰巧在一系列请求的中间失败,那么将数据置于无效状态可能会很糟糕。理想情况下,我会等待他们再次登录,重新发送失败的请求,然后继续。 【参考方案1】:

我认为这里最简单的解决方案是重新设计处理线程的方式。

我建议不要使用单个线程来处理请求,而是为每个请求生成一个新线程,然后让它返回状态代码(无论它是什么),然后您可以处理任何逻辑以通过身份验证重试您的主线程(即显示身份验证对话框,然后使用凭据重新生成身份验证线程)。

这还允许您更好地封装您的请求处理程序,这是一个很大的优势。为了正确封装此逻辑(因此您不必检查每个请求),您应该定义某种请求处理程序(类或函数)。例如

StatusCode make_reqeust(...) 
  // Deal with the logic on authentication here

StatusCode 是 HTTP 状态代码的类型。

当然,这并不能解决您的 UI 线程可能等待您的工作线程完成的问题,因此您还需要某种 UI 刷新方法,该方法每 x 时间调用一次并检查所有工作线程的状态线程(即通过检查返回的std::future's)。在这种情况下,您还想更改我上面的示例以可能产生一个单独的线程并返回一个std::future

【讨论】:

每个请求都有自己的线程。在处理每个响应之前,我总是可以检查状态,但我希望在内部处理 401 案例,所以在外面我可以担心响应内容。 内部?就像在一个函数中?还是一堂课? HTTP 请求流内部。否则,我每次处理请求时都必须检查并提示授权。理想情况下,我可以在返回响应之前拦截并处理请求(仅适用于 401),然后对其内容进行处理。 当然,所以把它放在一个函数中,在可能重试身份验证后返回最终代码 我可以同步封装它,但是如果发出异步请求(返回一个线程),则在加入之前不知道状态代码。此时,它已准备好处理其内容,并将提示身份验证的负担放在外部。如果我尝试在线程中进行任何检查,我们就会回到最初的问题。

以上是关于在主线程上启动对话框等待工作线程的结果的主要内容,如果未能解决你的问题,请参考以下文章

我可以在主线程(UI 线程)之外创建 UI 吗?

GCD - 如何在主线程上等待在主队列上执行的异步回调

如何在主线程上安全地使用[NSTask waitUntilExit]?

强制 Alamofire 在主线程上做所有事情?

java rxjava简化了Schedulers.io()中的List处理,并在主线程中等待结果

使用改造向请求添加标头时如何避免在主线程上等待?