【问题标题】:Complex flow with Observables带有 Observables 的复杂流
【发布时间】:2016-01-19 12:47:05
【问题描述】:

我有一系列图像 (IObservable<ImageSource>) 通过这个“管道”。

  1. 使用 OCR 识别每个图像
    • 如果结果具有有效值,则会将其上传到可以在给定时间(非同时)注册一组结果的服务。
    • 如果结果有任何无效值,则会将它们呈现给用户以进行修复。修复后,该过程将继续。
  2. 在此过程中,用户界面应保持响应。

问题是当用户必须交互时,我不知道如何处理这种情况。我就是不能这样做

        subscription = images                
            .Do(source => source.Freeze())
            .Select(image => OcrService.Recognize(image))                
            .Subscribe(ocrResults => Upload(ocrResults));

...因为当用户必须修复 ocrResults 时,应保持流程暂停直到接受有效值(即,用户可以执行单击按钮的命令)

怎么说:如果结果无效,等到用户修复它们?

【问题讨论】:

  • 我认为在您完成订阅过程之前,您无法将 imageSource 数据推送到 byte[] 数组中?这些方法是否依赖于该 ui 元素?
  • “由于我使用 ImageSource,我可能在不是 UI 线程的线程中遇到问题”。如果你Freeze()它就不会。
  • 它适用于冻结!谢谢!
  • 非常抱歉。我提出问题的方式并不代表我的保真度问题。我已经对其进行了编辑,现在它更适合了。
  • 听起来您需要暂停Observable。有办法做到这一点,例如this one(如果成功了,别忘了给作者点赞)

标签: c# .net wpf system.reactive observable


【解决方案1】:

这似乎是一个混合了 UX、WPF 和 Rx 的问题。试图只用 Rx 解决它可能会让你陷入困境。我相信你可以只用 Rx 来解决它,而不再考虑它,但你想要吗?它是否可测试、松耦合且易于维护?

根据我对问题的理解,您必须执行以下步骤

  1. 用户上传/选择一些图片
  2. 系统对每张图像执行 OCR
  3. 如果OCR工具认为图片来源有效,则上传处理结果
  4. 如果 OCR 工具认为图像源无效,则用户“修复”结果并上传结果

但这可能更好地描述为

  1. 用户上传/选择一些图片
  2. 系统对每张图像执行 OCR
  3. OCR 的结果被放入验证队列中
  4. 虽然结果无效,但需要用户手动将其更新为有效状态。
  5. 上传有效结果

所以在我看来,您需要一个基于任务/队列的 UI,以便用户可以看到他们需要处理的无效 OCR 结果。这也告诉我,如果涉及一个人,它可能应该在 Rx 查询之外。

第 1 步 - 执行 ORC

subscription = images                
        .Subscribe(image=>
        {
          //image.Freeze() --probably should be done by the source sequence
          var result = _ocrService.Recognize(image);
          _validator.Enqueue(result);
        });

第 2 步 - 验证结果

//In the Enqueue method, if queue is empty, ProcessHead();
//Else add to queue.
//When Head item is updated, ProcessHead();
//ProcessHead method checks if the head item is valid, and if it is uploads it and remove from queue. Once removed from queue, if(!IsEmpty) {ProcessHead();}


//Display Head of the Queue (and probably queue depth) to user so they can interact with it.

第 3 步 - 上传结果

Upload(ocrResults)

所以这里 Rx 只是我们武器库中的一个工具,而不是需要解决所有问题的一把锤子。我发现随着大多数“Rx”问题的规模不断扩大,Rx 只是充当各种Queue 结构的入口和出口点。这允许我们在我们的系统中使队列显式而不是隐式(即隐藏在 Rx 运算符中)。

【讨论】:

  • 谢谢你,李!我明白你的想法。但是,现在我的主要问题是上传阶段需要超过 2-3 秒并且 UI 挂起。我的第一反应是使上传方法异步,但随后我收到多个对 Upload 的并发调用。上传服务一次只支持一次上传。我怎样才能做到这一点?
  • 这是我的观点。如果你把工作分解成单独的部分,你就有更多的控制权。我想知道您是否有要上传的项目队列,为什么要从 UI 线程处理? UI 线程可以触发/启动一些后台进程来排空队列,但文件的实际上传将在后台线程上完成。您只能从 UI 线程更新上传的state(“待上传”->“上传”->“上传”)。
  • 在这个例子中你似乎也没有考虑错误流。例如如果 OCR 是在伪造文件上执行的,或者上传服务已关闭或不接受上传(太大、版本错误等)
【解决方案2】:

我假设您的 UploadAsync 方法返回一个 Task 以允许您等待它完成?如果是这样,则有处理任务的 SelectMany 重载。

images.Select(originalImage => ImageOperations.Resize(originalImage))
    .SelectMany(resizedImg => imageUploader.UploadAsync(resizedImg))
    .Subscribe();

【讨论】:

  • 非常抱歉。我提出问题的方式并不代表我的保真度问题。我已经对其进行了编辑,现在它更适合了。
【解决方案3】:

假设您有一个实现“用户修复过程”的异步方法:

/* show the image to the user, which fixes it, returns true if fixed, false if should be skipped */
async Task UserFixesTheOcrResults(ocrResults);

那么你的 observable 变成:

subscription = images                
        .Do(source => source.Freeze())
        .Select(image => OcrService.Recognize(image))
        .Select(ocrResults=> {
            if (ocrResults.IsValid)
                return Observable.Return(ocrResults);
            else
                return UserFixesTheOcrResults(ocrResults).ToObservable().Select(_ => ocrResults)
        })
        .Concat()             
        .Subscribe(ocrResults => Upload(ocrResults));

【讨论】:

  • 看起来不错,但这会让流程暂停吗?我假设如果images 在需要用户输入后发出新值,则以下值通常会通过 monad
  • 它看起来非常优雅,可读,但问题是我不知道如何转换用户交互。在 UserFixTheOcrResults 中,结果显示在窗口中,当一切正常时,他们单击按钮提交正确的结果。我该怎么等? :( 抱歉,但我对这种情况不是很有经验。绝对是这里的 Rx 菜鸟。
  • 另外,用户必须修复 ocrResults,所以它不应该是一个 Tas,而只是一个 Task :)
  • @SuperJMN 实现 UserFixesTheOcrResults 方法更像是 WPF 问题而不是 Rx 问题。您可以使用reactiveui 库中的ReactiveCommand 并执行以下操作:var viewModel = /*some VM*/;ShowFixWindowFor(viewModel);await viewModel.ValidateCommand;(如果您选择ReactiveCommand,则可以直接将其用作可观察对象,并完全避免使用Task
  • @supertopi,可能是,但 AFAICT 的要求是按顺序上传而不是同时上传,但 OCR 可以与 UI 并行运行(例如,因为 UI 需要在此期间保持响应),如果是这样的话并非如此,由于 Rx.Net 中缺乏背压变得更加困难,而复杂的 Switch(您上面的链接)似乎是纯 Rx 中的唯一选择。
猜你喜欢
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2015-06-21
  • 1970-01-01
  • 2017-05-05
  • 2018-07-09
  • 2019-12-14
  • 1970-01-01
相关资源
最近更新 更多