本文介绍了IScheduler.Schedule 与 IScheduler.ScheduleAsync?的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

IScheduler 接口提供

The IScheduler interface provides

public static IDisposable Schedule(this IScheduler scheduler, Action action)

public static IDisposable ScheduleAsync(this IScheduler scheduler, Func<IScheduler, CancellationToken, System.Threading.Tasks.Task<IDisposable>> action)

ScheduleAsync 的方法说明:

The method description for ScheduleAsync:

    // Summary:
    //     Schedules work using an asynchronous method, allowing for cooperative scheduling
    //     in an imperative coding style.
    //
    // Parameters:
    //   scheduler:
    //     Scheduler to schedule work on.
    //
    //   action:
    //     Asynchronous method to run the work, using Yield and Sleep operations for
    //     cooperative scheduling and injection of cancellation points.
    //
    // Returns:
    //     Disposable object that allows to cancel outstanding work on cooperative cancellation
    //     points or through the cancellation token passed to the asynchronous method.
    //
    // Exceptions:
    //   System.ArgumentNullException:
    //     scheduler or action is null.

有人可以解释这两种方法之间的区别吗?

Can someone explain the differences between the 2 methods?

我应该什么时候使用 ScheduleAsync?

When should i use ScheduleAsync?

我应该什么时候使用Schedule?

and when should i use Schedule?

在命令式编码风格中允许协作调度意味着什么?

What does it means by allowing for cooperative scheduling in an imperative coding style?

谢谢.

推荐答案

转发

此答案基于 this 帖子 - 注意它很长并且涵盖的远不止这一点.转到标题为在 Rx 查询运算符中利用异步" 的部分,所有内容都进行了解释,包括标题为 使调度程序更容易与await"一起使用

Forward

This answer is based on the definitive explanation direct from the Rx team in this post - caution it's long and covers much more than just this point. Go down to the section entitled Leveraging "async" in Rx query operators and all is explained, including a specific example on ScheduleAsyc in the section titled Making schedulers easier to use with "await"

这是我试图解释的:

ScheduleAsync 的主要动机是采用 C# 5 的 async/await 特性,以简化执行公平"操作的代码的编写.许多事件的调度,否则可能会导致其他操作的调度程序饥饿.这就是合作调度"的意思.- 与共享调度程序的其他代码一起玩得很好.为此,您可以安排下一个事件,然后让出控制权,直到该事件触发并连接到该事件以安排您的下一个事件,依此类推.

The primary motivation for ScheduleAsync is to embrace the async/await feature of C# 5 to simplify writing code that performs "fair" scheduling of many events that might otherwise cause scheduler starvation of other operations. This is what is meant by "cooperative scheduling" - playing nice with other code sharing the scheduler. You do this by scheduling the next event, and then yielding control until that event fires and hooking in to that event to schedule your next event and so on.

在 Rx 2.0 之前,这是通过递归调度实现的.

Prior to Rx 2.0 this was achieved via recursive scheduling.

这是链接文章中的示例,它给出了 Range 运算符的实现.这个实现很糟糕,因为它没有让出控制,让调度器饿死:

Here is the example from the linked article that gives an implementation of the Range operator. This implementation is poor because it starves the scheduler by not yielding control:

static IObservable<int> Range(int start, int count, IScheduler scheduler)
{
    return Observable.Create<int>(observer =>
    {
        return scheduler.Schedule(() =>
        {
            for (int i = 0; i < count; i++)
            {
                Console.WriteLine("Iteration {0}", i);
                observer.OnNext(start + i);
            }
            observer.OnCompleted();
        });
    });
}

请注意 OnNext 是如何在不放弃控制的情况下重锤调度程序的循环中的(如果调度程序是单线程的,则尤其糟糕).它使其他操作没有机会安排其操作,并且不允许在取消时中止.我们如何解决这个问题?

Notice how the OnNext sits in a loop hammering the scheduler without yielding control (particularly bad if the scheduler is single threaded). It starves other operations of the chance to Schedule their actions, and it doesn't allow for aborting in the event of cancellation. How can we solve this?

这是用递归调度解决这个问题的旧方法 - 很难看出发生了什么.它不是命令式编码风格".递归调用 self() 在你第一次看到它时是非常不透明的——在我的例子中是第十次,尽管我最终得到了它.这个传奇人物 Bart de Smet 的经典帖子将告诉您有关此技术的更多信息.无论如何,这是递归样式:

Here is the old way this was tackled with recursive scheduling - it's quite hard to see what's going on. It's not an "imperative coding style". The recursive call self() is pretty brain-meltingly opaque the first time you see it - and the tenth in my case, although I got it eventually. This classic post by the legendary Bart de Smet will tell you more than you'll ever need to know about this technique. Anyway, here's the recursive style:

static IObservable<int> Range(int start, int count, IScheduler scheduler)
{
    return Observable.Create<int>(observer =>
    {
        return scheduler.Schedule(0, (i, self) =>
        {
            if (i < count)
            {
                Console.WriteLine("Iteration {0}", i);
                observer.OnNext(start + i);
                self(i + 1); /* Here is the recursive call */
            }
            else
            {
                observer.OnCompleted();
            }
        });
    });
}

为了更公平,如果订阅被释放,下一个待处理的预定操作将被取消.

As well as being fairer, the next pending scheduled action will be canceled if the subscription is disposed.

这是通过 async/await 的编译器转换实现延续的新方法,它允许命令式编码风格".请注意,与递归风格相比,其动机更易读 - async/await 突出显示了正在发生的事情,通常以 .NET 惯用的方式:

Here is the new way with continuations via compiler transformation of async/await which allows the "imperative coding style". Note that the motivation compared with the recursive style is greater legibility - the async/await stand out showing what's happening in a way idiomatic to .NET in general:

static IObservable<int> Range(int start, int count, IScheduler scheduler)
{
    return Observable.Create<int>(observer =>
    {
        return scheduler.ScheduleAsync(async (ctrl, ct) =>
        {
            for (int i = 0; i < count; i++)
            {
                Console.WriteLine("Iteration {0}", i);
                observer.OnNext(i);
                await ctrl.Yield(); /* Use a task continuation to schedule next event */
            }
            observer.OnCompleted();

            return Disposable.Empty;
        });
    });
}

它看起来就像一个 for 循环,但实际上 await ctrl.Yield() 将放弃控制,允许其他代码进入调度程序.它使用 Task continuations 一次只安排一个事件——也就是说,每次迭代只在前一次完成时发布到调度程序,避免直接在调度程序上排长队.取消也有效,这次 Rx 框架将订阅的处理转换为通过 ct 传入的取消令牌.

It looks just like a for-loop, but in reality the await ctrl.Yield() is going to yield control allowing other code to get at the scheduler. It uses Task continuations to only schedule events one at a time - that is each iteration is only posted to the scheduler when the previous one is done, avoiding long queues directly on the scheduler. Cancelation also works, this time the Rx framework translates the disposal of the subscription on to a cancellation token passed in via ct.

我建议阅读 原帖 如果链接还不错,我就从这里拿走了这个!

I recommend reading the original post I took this from if the link is still good!

这篇关于IScheduler.Schedule 与 IScheduler.ScheduleAsync?的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持!

10-20 20:34