回调如何在非阻塞设计中起作用?

时间:2015-03-27 14:46:32

标签: multithreading scala asynchronous callback nonblocking

看了几个其他问题,但没有找到我想要的东西。我使用Scala但我的问题非常高,所以希望对任何语言都不可知。


常规方案:

  1. Thread A运行一个函数,并且有一些阻塞工作要做(比如数据库调用)。
  2. 该函数有一些非阻塞代码(例如Scala中的Async块)导致某种“工作者”#39; Thread B(在不同的池中)接收I / O任务。
  3. Thread A中的方法完成返回最终将包含结果的Future,并将Thread A返回到其池中以快速获取另一个要处理的请求。
  4. Q1。有些线程通常需要等待?

    我对非阻塞体系结构的理解是,常见的方法是在I / O工作的某个地方仍然有一些线程等待/阻塞 - 这只是一个拥有不同池的访问不同内核以便小的情况请求处理线程的数量可以管理大量的并发请求,而无需等待CPU核心。

    这是正确的一般理解吗?

    Q2。回调如何运作?

    在上面的场景中 - 执行I / O工作的Thread B将运行回调函数(由线程A提供),如果/当I / O工作完成时 - 将完成{{1}有一些结果。

    线程A现在正在执行其他操作,并且不再与原始请求建立关联。未来的结果如何被发送回客户端套接字?我知道不同的语言有不同的这种机制的实现,但在很高的层次上,我目前的假设是(无论语言/框架如何)一些框架/容器对象必须始终做某种编排,以便当Future任务是完成后,Result将被发送回处理请求的原始套接字。


    我花了好几个小时试图找到可以解释这个问题的文章,但每篇文章似乎只是处理真正的低级细节。我知道我错过了一些细节,但我很难问我的问题,因为我不太确定我缺少哪些部分:)

2 个答案:

答案 0 :(得分:4)

Q1:不,至少不是在用户代码级别。希望您的异步I / O最终归结为异步内核API(例如select())。反过来,它将使用DMA来执行I / O并在完成后触发中断。所以它至少在硬件级别上是异步的。

Q2:主题B完成Future。如果您正在使用类似onComplete的内容,那么线程B将触发该操作(可能通过创建新任务并将该任务交给线程池以便稍后将其提取)作为完成调用的一部分。如果另一个线程调用Await来阻止Future,它将触发该线程恢复。如果还没有访问过Future,则没有特别的事情发生 - 该值位于Future中,直到某些内容使用它为止。 (请参阅PromiseCompletingRunnable了解详细信息 - 令人惊讶的可读性。)

答案 1 :(得分:4)

  

我对非阻塞体系结构的理解是,常见的方法是在某处的I / O工作上仍然有一些线程等待/阻塞

如果一个线程在某个地方被阻塞,那么它实际上并不是一个非阻塞的架构。所以不,这不是对它的正确理解。这并不意味着这一定是坏事。有时你只需要处理阻塞(例如使用JDBC)。将它推送到指定用于阻塞的固定线程池中会更好,而不是让整个应用程序遭受线程饥饿。

  

线程A现在正在执行其他操作,并且不再与原始请求建立关联。将来的结果如何发送回客户端套接字?

使用Future,它实际上取决于ExecutionContext。创建Future时,工作完成取决于ExecutionContext

val f: Future[?] = ???
val g: Future[?] = ???
立即创建

fg,并将工作提交到ExecutionContext中的任务队列。在大多数情况下,我们无法保证哪些实际上会先执行或完成。你对价值观所做的事情很好。显然,如果您使用Await等待Future的完成,那么我们将阻止当前线程。如果我们map他们并对值执行某些操作,那么我们再次需要另一个ExecutionContext来提交任务。这为我们提供了一系列任务,这些任务异步提交并重新提交给执行程序,以便在每次操作Future时执行。

最终,在该链的末尾需要有一些onComplete来将该值的传递返回给某些东西,无论是写入流还是其他东西。即,它可能不在原始线程的手中。