Warning: file_get_contents(/data/phpspider/zhask/data//catemap/2/csharp/285.json): failed to open stream: No such file or directory in /data/phpspider/zhask/libs/function.php on line 167

Warning: Invalid argument supplied for foreach() in /data/phpspider/zhask/libs/tag.function.php on line 1116

Notice: Undefined index: in /data/phpspider/zhask/libs/function.php on line 180

Warning: array_chunk() expects parameter 1 to be array, null given in /data/phpspider/zhask/libs/function.php on line 181
C# 从操作创建可取消的IObservable_C#_System.reactive - Fatal编程技术网

C# 从操作创建可取消的IObservable

C# 从操作创建可取消的IObservable,c#,system.reactive,C#,System.reactive,我想创建一个实用程序方法,为一个只在订阅时调用的操作创建一个IObservable!它遵循SubscribeOn(…)指令。以下是我的实现,它基于我可以从中提取的内容和其他资源,但在一个特定情况下失败: /// <summary> /// Makes an observable out of an action. Only at subscription the task will be executed. /// </summary>

我想创建一个实用程序方法,为一个只在订阅时调用的操作创建一个IObservable!它遵循SubscribeOn(…)指令。以下是我的实现,它基于我可以从中提取的内容和其他资源,但在一个特定情况下失败:

    /// <summary>
    /// Makes an observable out of an action. Only at subscription the task will be executed. 
    /// </summary>
    /// <param name="action">The action.</param>
    /// <returns></returns>
    public static IObservable<Unit> MakeObservable_2(Action action)
    {
        return Observable.Create<Unit>(
            observer =>
            {
                return System.Reactive.Concurrency.CurrentThreadScheduler.Instance.Schedule(
                    () =>
                    {
                        try
                        {
                            action();
                            observer.OnNext(Unit.Default);
                            observer.OnCompleted();
                        }
                        catch (Exception ex)
                        {
                            observer.OnError(ex);
                        }
                    });
            });
    }

我认为这是可以观察到的。开始是你要寻找的。

我认为您可以使代码更简单,也可以使测试更简单。Rx的妙处在于,您应该能够消除所有任务/线程/手动重置事件。我还假设您也可以使用NUnit的[Timeout]属性而不是自定义代码

无论如何。。。 @Per是正确的,可观察的。Start是您所寻找的。你给它一个动作和一个IScheduler,这似乎正是你想要的

[Test]
public void Run_Action_as_IOb_on_scheduler_with_ObStart()
{
    var scheduler = new TestScheduler();
    var flag = false;
    Action action = () => { flag = true; };

    var subscription = Observable.Start(action, scheduler)
                                    .Subscribe();

    Assert.IsFalse(flag);
    scheduler.AdvanceBy(1);
    Assert.IsTrue(flag);
    subscription.Dispose(); //Not required as the sequence will have completed and then auto-detached.
}
然而,你可能会注意到它确实有一些奇怪的行为(至少在我这台电脑上的V1中)。具体地说,Observable.Start将立即运行操作,而不是实际等待订阅Observable序列。同样由于这个原因,调用subscribe,然后在应该执行的操作之前处理订阅是无效的。嗯

[Test]
public void Run_Action_as_IOb_on_scheduler_with_ObStart_dispose()
{
    var scheduler = new TestScheduler();
    var flag = false;
    Action action = () => { flag = true; };

    var subscription = Observable.Start(action, scheduler).Subscribe();


    Assert.IsFalse(flag);
    subscription.Dispose();
    scheduler.AdvanceBy(1);
    Assert.IsFalse(flag);   //FAILS. Oh no! this is true!
}
[Test]
public void Run_Action_as_IOb_on_scheduler_with_ObStart_no_subscribe()
{
    var scheduler = new TestScheduler();
    var flag = false;
    Action action = () => { flag = true; };

    Observable.Start(action, scheduler);
    //Note the lack of subscribe?!

    Assert.IsFalse(flag);
    scheduler.AdvanceBy(1);
    Assert.IsFalse(flag);//FAILS. Oh no! this is true!
}
但是,我们可以按照您的方式使用Observable.Create。但是,您非常接近,不需要在Create委托中执行任何调度。只要相信Rx会为你做这件事

[Test]
public void Run_Action_as_IOb_on_scheduler_with_ObCreate()
{
    var scheduler = new TestScheduler();
    var flag = false;
    Action action = () => { flag = true; };

    var subscription = Observable.Create<Unit>(observer =>
        {
            try
            {
                action();
                observer.OnNext(Unit.Default);
                observer.OnCompleted();
            }
            catch (Exception ex)
            {
                observer.OnError(ex);
            }
            return Disposable.Empty;
        })
        .SubscribeOn(scheduler)
        .Subscribe();   //Without subscribe, the action wont run.

    Assert.IsFalse(flag);
    scheduler.AdvanceBy(1);
    Assert.IsTrue(flag);
    subscription.Dispose(); //Not required as the sequence will have completed and then auto-detached.
}

[Test]
public void Run_Action_as_IOb_on_scheduler_with_ObCreate_dispose()
{
    var scheduler = new TestScheduler();
    var flag = false;
    Action action = () => { flag = true; };

    var subscription = Observable.Create<Unit>(observer =>
    {
        try
        {
            action();
            observer.OnNext(Unit.Default);
            observer.OnCompleted();
        }
        catch (Exception ex)
        {
            observer.OnError(ex);
        }
        return Disposable.Empty;
    })
        .SubscribeOn(scheduler)
        .Subscribe();   //Without subscribe, the action wont run.

    Assert.IsFalse(flag);
    subscription.Dispose();
    scheduler.AdvanceBy(1);
    Assert.IsFalse(flag);   //Subscription was disposed before the scheduler was able to run, so the action did not run.
}
如果你运行这个,你会注意到控制台上甚至没有打印任何内容,因此我们没有竞争条件,操作就是从不运行。原因是调度器尚未启动其消息循环。为了纠正这个测试,我们必须用混乱的基础结构代码填充它

[Test, Timeout(2000)]
public void Testing_with_Dispatcher_BeginInvoke()
{
    var frame = new DispatcherFrame();  //1 - The Message loop
    var wasRun = false;
    Action MyAction = () =>
    {
        Console.WriteLine("Running...");
        wasRun = true;
        Console.WriteLine("Run.");
        frame.Continue = false;         //2 - Stop the message loop, else we hang forever
    };
    Dispatcher.CurrentDispatcher.BeginInvoke(MyAction);

    Dispatcher.PushFrame(frame);        //3 - Start the message loop

    Assert.IsTrue(wasRun);
}
因此,我们显然不希望对所有需要WPF中并发性的测试都这样做。试图将frame.Continue=false注入到我们无法控制的操作中是一场噩梦。幸运的是,IScheudler通过它的调度方法公开了我们所需要的一切


接下来,CurrentThreadScheduler应该被看作是一个蹦床,而不是一个SynchronizationContext(我想你是这么认为的)。

非常感谢您提供的详细信息。我将您的答案标记为有用,特别是对于测试调度程序演示。然而,我仍然不是很满意,我相信我的大部分问题仍然没有答案。虽然我同意我可以取消Task/Thread/ManualResetEvent,但我相信我的单元测试在代码和公式化的期望方面是正确的(你同意还是看到了任何问题?),我真的希望它们通过。同样的测试在MakeObservable_3中失败。这很关键,因为我认为
.SubscribeOn(Dispatcher.CurrentDispatcher)
应该正确执行。也许我还没有完全理解Rx。我不喜欢Observable.Create解决方案的一个事实是,try块的内容是在Disposable.Empty返回之前执行的。这种方法从不允许在订阅过程中通过
Dispose()
取消某些内容。
Observable.Start
-bug在2.0.20612.0中仍然可用。您是否有解释为什么
Observable.SubscribeOn(Dispatcher.CurrentDispatcher.Subscribe())
RxActionUtilities\u MakeObservableFromAction\u worksaseExpected()中失败。
我要求太多了吗?
MakeObservable(Action Action,IsScheduler scheduler)
代替
MakeObservable
+
SubscribeOn
是更好的方法吗?嗯。
公共静态IObservable MakeObservable_4(Action Action Action){返回Observable.Create(Observable=>{try{return Observable.Start(action.Subscribe(observer);}catch(Exception ex){observer.OnError(ex);return Disposable.Empty;}}}}
未能通过相同的单元测试…我认为您应该使用Rx/IsScheduler并利用取消和递归功能(),或者,如果需要,您应该使用Task/CancellationToken并可能转换为Rx with Task.ToObservable()。
[Test]
public void Run_Action_as_IOb_on_scheduler_with_ObStart_dispose()
{
    var scheduler = new TestScheduler();
    var flag = false;
    Action action = () => { flag = true; };

    var subscription = Observable.Start(action, scheduler).Subscribe();


    Assert.IsFalse(flag);
    subscription.Dispose();
    scheduler.AdvanceBy(1);
    Assert.IsFalse(flag);   //FAILS. Oh no! this is true!
}
[Test]
public void Run_Action_as_IOb_on_scheduler_with_ObStart_no_subscribe()
{
    var scheduler = new TestScheduler();
    var flag = false;
    Action action = () => { flag = true; };

    Observable.Start(action, scheduler);
    //Note the lack of subscribe?!

    Assert.IsFalse(flag);
    scheduler.AdvanceBy(1);
    Assert.IsFalse(flag);//FAILS. Oh no! this is true!
}
[Test]
public void Run_Action_as_IOb_on_scheduler_with_ObCreate()
{
    var scheduler = new TestScheduler();
    var flag = false;
    Action action = () => { flag = true; };

    var subscription = Observable.Create<Unit>(observer =>
        {
            try
            {
                action();
                observer.OnNext(Unit.Default);
                observer.OnCompleted();
            }
            catch (Exception ex)
            {
                observer.OnError(ex);
            }
            return Disposable.Empty;
        })
        .SubscribeOn(scheduler)
        .Subscribe();   //Without subscribe, the action wont run.

    Assert.IsFalse(flag);
    scheduler.AdvanceBy(1);
    Assert.IsTrue(flag);
    subscription.Dispose(); //Not required as the sequence will have completed and then auto-detached.
}

[Test]
public void Run_Action_as_IOb_on_scheduler_with_ObCreate_dispose()
{
    var scheduler = new TestScheduler();
    var flag = false;
    Action action = () => { flag = true; };

    var subscription = Observable.Create<Unit>(observer =>
    {
        try
        {
            action();
            observer.OnNext(Unit.Default);
            observer.OnCompleted();
        }
        catch (Exception ex)
        {
            observer.OnError(ex);
        }
        return Disposable.Empty;
    })
        .SubscribeOn(scheduler)
        .Subscribe();   //Without subscribe, the action wont run.

    Assert.IsFalse(flag);
    subscription.Dispose();
    scheduler.AdvanceBy(1);
    Assert.IsFalse(flag);   //Subscription was disposed before the scheduler was able to run, so the action did not run.
}
public static class RxActionUtilities
{
    /// <summary>
    /// Makes an observable out of an action. Only at subscription the task will be executed. 
    /// </summary>
    /// <param name="action">The action.</param>
    /// <returns></returns>
    /// <example>
    /// <code>
    /// <![CDATA[
    /// RxActionUtilities.MakeObservable_3(myAction)
    ///                  .SubscribeOn(_schedulerProvider.TaskPoolScheduler)
    ///                  .Subscribe(....);
    /// 
    /// ]]>
    /// </code>
    /// </example>
    public static IObservable<Unit> MakeObservable_3(Action action)
    {
        return Observable.Create<Unit>(observer =>
            {
                try
                {
                    action();
                    observer.OnNext(Unit.Default);
                    observer.OnCompleted();
                }
                catch (Exception ex)
                {
                    observer.OnError(ex);
                }
                return Disposable.Empty;
            });
    }
}
[Test, Timeout(2000)]
public void DispatcherFail()
{
    var wasRun = false;
    Action MyAction = () =>
        {
            Console.WriteLine("Running...");
            wasRun = true;
            Console.WriteLine("Run.");
        };
    Dispatcher.CurrentDispatcher.BeginInvoke(MyAction);

    Assert.IsTrue(wasRun);
}
[Test, Timeout(2000)]
public void Testing_with_Dispatcher_BeginInvoke()
{
    var frame = new DispatcherFrame();  //1 - The Message loop
    var wasRun = false;
    Action MyAction = () =>
    {
        Console.WriteLine("Running...");
        wasRun = true;
        Console.WriteLine("Run.");
        frame.Continue = false;         //2 - Stop the message loop, else we hang forever
    };
    Dispatcher.CurrentDispatcher.BeginInvoke(MyAction);

    Dispatcher.PushFrame(frame);        //3 - Start the message loop

    Assert.IsTrue(wasRun);
}