订阅可观察(主题)的Reactive Extensions

时间:2015-08-19 14:10:53

标签: task-parallel-library system.reactive

我只是在winforms应用程序中第一次使用Reactive Extensions。请注意,过去4年我一直在进行网络开发,而且我对淘汰赛中的观察能力和可观察模式非常熟悉,我猜这是造成我的困惑。

无论如何,问题和代码。我有一个简单的winforms实验(见下文),我正在建立这个实验来说明我的问题。下面的订阅直到启动new中的线程完成后才运行。我可以跟踪它对OnNext的调用,但订阅根本不会激活,直到有时20-30秒。有人可以向我解释这种行为吗?

public partial class Form1 : Form
{

    private Subject<int> progress;
    private CancellationToken cancellationToken;
    private IScheduler _scheduler;

    public Form1()
    {
        InitializeComponent();

        CancellationTokenSource source = new CancellationTokenSource();
        cancellationToken = source.Token;

        _scheduler = new SynchronizationContextScheduler(SynchronizationContext.Current);


    }

    private void Start_Click(object sender, EventArgs e)
    {
        progress
            .ObserveOn(_scheduler)
            //.Throttle(TimeSpan.FromSeconds(5))
            .Subscribe(
                (i) => {
                    progressBar1.Do<ProgressBar>(ctl =>
                    {
                        ctl.Value = i;
                    });
                },
                (ex) => { },
            cancellationToken
        );

        Task counterTask = Task.Factory.StartNew(() =>
        {
            for (var i = 1; i < 101; i++)
            {
                Thread.Sleep(500);
                progress.OnNext(i);
            }
        }, cancellationToken,
        TaskCreationOptions.LongRunning,
        TaskScheduler.FromCurrentSynchronizationContext()
      );
    }

    private void Form1_Load(object sender, EventArgs e)
    {
       progress = new Subject<int>();
    }


}



public static class ControlExtensions
{
    public static void Do<TControl>(this TControl control, Action<TControl> action)
      where TControl : Control
    {
        if (control.InvokeRequired)
            control.Invoke(action, control);
        else
            action(control);
    }
}

1 个答案:

答案 0 :(得分:0)

您的问题来自于您的任务在UI线程上运行,因为您正在使用TaskScheduler.FromCurrentSynchronizationContext()

因此,您的各种Sleep调用阻塞了UI线程,冻结了UI(例如无法拖动窗口)并阻止您的可观察订阅执行(因为ObserveOn,它应该执行在UI线程调度程序上)。

TaskScheduler.FromCurrentSynchronizationContext()替换为TaskScheduler.Default(后台TaskPool线程),一切都会按预期工作。

请注意,您对Do / Invoke的调用是不必要的,因为您已经通过自己提供的调度程序使用了UI线程。