在RxPY中使用from_iterable / range的subscribe_on

时间:2015-09-08 06:44:06

标签: python scheduler reactive-programming rx-py

我试图了解python的反应式扩展中的调度问题。我想使用subscribe_on并行处理多个observable。如果使用just创建了observable,则此方法可以正常工作,但如果使用rangefrom_则不行。

just默认为Scheduler.immediate,而其他生成器默认为Scheduler.current_thread。这导致了差异,但感觉与我不一致。可能是因为我没有掌握完整的问题 请考虑以下示例:

import rx
from rx.concurrency.scheduler import Scheduler
import time
import threading


def work(x):
    print "processing %s on thread %s" % (x, threading.currentThread().name)
    time.sleep(1)


def finish(x):
    print "finished %s on thread %s" % (x, threading.currentThread().name)


# Creates new thread (I like)
rx.Observable.just(3)\
    .do_action(work)\
    .subscribe_on(Scheduler.new_thread)\
    .subscribe(finish)

# Runs on MainThread (I don't like)
rx.Observable.range(1, 3) \
    .do_action(work) \
    .subscribe_on(Scheduler.new_thread) \
    .subscribe(finish)

它适用于observe_on或者如果调度程序直接传递给生成器,但我想将可观察的创建与处理分离并实现如下所示:

import rx
from rx.concurrency.scheduler import Scheduler
import time
import threading


def work(x):
    print "processing %s on thread %s" % (x, threading.currentThread().name)
    time.sleep(1)


def finish(x):
    print "finished %s on thread %s" % (x, threading.currentThread().name)


def factory_single():
    return rx.Observable.just(1).do_action(work)


def factory_multiple():
    return rx.Observable.range(2, 4).do_action(work)


def process(factory):
    factory().subscribe_on(Scheduler.new_thread).subscribe(finish)

# Creates a new thread (I like)
process(factory_single)

# Runs on MainThread (I don't like)
process(factory_multiple)

我误解了subscribe_on吗?我的做法错了吗?

1 个答案:

答案 0 :(得分:2)

您的示例中有三个可以独立调度的操作:

  1. 数据Feed操作。 justrange默认使用不同的调度程序,但它们之间没有太大区别。两者都在当前线程上提供初始值。您可以通过将默认调度程序作为参数传递给这些方法来覆盖它们的默认调度程序。

  2. 订阅操作。默认情况下使用Scheduler.current_thread。即它在与数据馈送操作相同的线程上执行。可以通过subscribe_on方法覆盖。

  3. 观察(on_nexton_erroron_completed)操作。默认情况下使用Scheduler.current_thread。即它在与订阅操作相同的线程上执行。可以通过observe_on方法覆盖。

  4. 如果您仅对其中一个操作覆盖调度程序,则其他操作将如上所述。

    关于计划程序

    Scheduler.immediate并没有真正安排任何事情。它会立即在调度它的同一个线程上调用操作。

    Scheduler.current_thread通过排队操作避免递归,但仍然在调度它的同一线程上调用操作。

    Scheduler.new_thread启动单个后台线程,一个接一个地执行操作。

    Scheduler.timeout为每个需要执行的操作启动新的后台线程。

    尝试并行处理

    在不同线程中调度工作的最合适方法似乎是observe_on

    问题是现在RxPy中没有thread_pool调度程序。 new_thread调度程序只启动一个线程,因此对您没有多大帮助。

    timeout调度程序可用于并行,但它不能控制并发线程的数量,因此并发任务数量的爆炸性增长可能会溢出内存并有效地使系统崩溃。

    不是observe_on

    中的错误

    我尝试使用observe_on(Scheduler.timeout)运行您的示例,但任务仍未并行执行。在查看RxPy源代码后,我发现它只在当前事件完成后才调度下一个事件,这有效地禁用了并行处理。我的第一反应是在observe_on实施中报告bug

    但经过进一步调查后,我发现串行执行不是错误,而是intended behavior

    并行执行任务的正确方法

    以下是有效的代码(基于this answer):

    Observable.range(1, 3) \
      .select_many(lambda i: Observable.start(lambda: work(i), scheduler=Scheduler.timeout)) \
      .observe_on(Scheduler.event_loop) \
      .subscribe(finish)
    

    Observable.start创建异步 observable,通过Scheduler.timeout在单独的线程上安排。

    observe_on(Scheduler.event_loop)是可选的。它强制finish方法在同一个线程上调用所有项目。

    请注意,我们无法保证在初始finish订单中调用range方法。