Task.Yield - 真正的用途? [英] Task.Yield - real usages?

查看:349
本文介绍了Task.Yield - 真正的用途?的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

我一直在阅读有关 Task.Yield ,作为一个JavaScript开发,我可以告诉大家,就是它的工作是<罢工>的究竟的< /行使>同的setTimeout(函数(){...},0); 在让主单线程处理又名其他的东西方面:

I've been reading about Task.Yield , And as a Javascript developer I can tell that's it's job is exactly the same as setTimeout(function (){...},0); in terms of letting the main single thread deal with other stuff aka :

不把所有的力量,释放不时时间 - 所以别人会
  有一些太...

"don't take all the power , release from time time - so others would have some too..."

在JS它在长期循环工作尤其如此。 (不要让浏览器冻结... 的)

In js it's working particular in long loops. ( don't make the browser freeze...)

但是我看到这个例子<一个href=\"http://books.google.co.il/books?id=qgq8T4X3erIC&pg=PA574&lpg=PA574&dq=%22the%20task.yield%20method%20creates%22&source=bl&ots=94eP29Nmls&sig=XxEvfX6PVmXhdOdOJC7dlJUGf0I&hl=en&sa=X&ei=t7ZjU5WTJoaxO-f4gXg&ved=0CCYQ6AEwAA#v=onepage&q=%22the%20task.yield%20method%20creates%22&f=false\">here

public static async Task < int > FindSeriesSum(int i1)
{
    int sum = 0;
    for (int i = 0; i < i1; i++)
    {
        sum += i;
        if (i % 1000 == 0) ( after a bulk , release power to main thread)
            await Task.Yield();
    }

    return sum;
}

作为一个程序员JS我能理解他们这里做过的事。

As a JS programmer I can understand what they did here.

但作为一个C#程序员,我问自己:为什么不打开一个任务吧。

BUT as a C# programmer I ask myself : why not open a task for it ?

 public static async Task < int > FindSeriesSum(int i1)
    {
         //do something....
         return await MyLongCalculationTask();
         //do something
    }

将JS我无法打开任务( 是的,我知道我与网络工作者可实际上 的)。但随着C#的我可以

With Js I can't open a Task (yes i know i can actually with web workers) . But with c# I can.

如果这样 - 为什么甚至不时释放时间,而我可以释放它在所有麻烦

If So -- why even bother with releasing from time to time while I can release it at all ?

添加引用:

从<一个href=\"http://books.google.co.il/books?id=qgq8T4X3erIC&pg=PA574&lpg=PA574&dq=%22the%20task.yield%20method%20creates%22&source=bl&ots=94eP29Nmls&sig=XxEvfX6PVmXhdOdOJC7dlJUGf0I&hl=en&sa=X&ei=t7ZjU5WTJoaxO-f4gXg&ved=0CCYQ6AEwAA#v=onepage&q=%22the%20task.yield%20method%20creates%22&f=false\">here :

From here :

从<一个href=\"http://books.google.co.il/books?id=I-dHxFjfbi8C&pg=PA65&lpg=PA65&dq=%22task.yield%28%29%22%20%20books&source=bl&ots=QwX60qDOeN&sig=6YlwxL1aX_OuYzpU8ENoYUM8TR0&hl=en&sa=X&ei=O5lkU5zKBumM7Qbqx4BY&ved=0CDIQ6AEwAQ#v=onepage&q=%22task.yield%28%29%22%20%20books&f=false\">here (另一种电子书):

From here (another ebook):

推荐答案

当您看到:

await Task.Yield();

您可以考虑一下这种方式:

you can think about it this way:

await Task.Factory.StartNew( 
    () => {}, 
    CancellationToken.None, 
    TaskCreationOptions.None, 
    SynchronizationContext.Current != null?
        TaskScheduler.FromCurrentSynchronizationContext(): 
        TaskScheduler.Current);

这一切确实是可以确保的延续将在未来异步发生。通过的异步的我的意思是执行控制将返回到的调用者异步方法,并且延续回调的发生相同的堆栈帧。

All this does is makes sure the continuation will happen asynchronously in the future. By asynchronously I mean that the execution control will return to the caller of the async method, and the continuation callback will not happen on the same stack frame.

究竟何时在什么线程将彻底发生取决于调用者线程的同步上下文。

When exactly and on what thread it will happen completely depends on the caller thread's synchronization context.

对于UI线程后,继续将发生在未来的某个消息循环迭代,由 Application.Run 运行(<一个href=\"https://msdn.microsoft.com/en-us/library/system.windows.forms.application.run(v=vs.110).aspx\">WinForms)或 Dispatcher.Run (<一个href=\"https://msdn.microsoft.com/en-us/library/system.windows.threading.dispatcher.run(v=vs.110).aspx\">WPF).在内部,它归结为Win32的 PostMessage的 API,它发布自定义消息到UI线程的消息队列。该的await 当这个消息被抽到和加工延续回调将会被调用。你完全失控什么时候这个事情发生的。

For a UI thread, the continuation will happen upon some future iteration of the message loop, run by Application.Run (WinForms) or Dispatcher.Run (WPF). Internally, it comes down to the Win32 PostMessage API, which post a custom message to the UI thread's message queue. The await continuation callback will be called when this message gets pumped and processed. You're completely out of control about when exactly this is going to happen.

此外,Windows有抽文对自己的优先事项: INFO:窗口消息优先级的。最相关的部分:

Besides, Windows has its own priorities for pumping messages: INFO: Window Message Priorities. The most relevant part:

在此计划下,优先级可以考虑三电平。所有
  发布的消息比用户输入信息的优先级更高,因为
  它们驻留在不同的队列。和所有的用户输入消息
  优先级高于WM_PAINT和WM_TIMER消息更高。

Under this scheme, prioritization can be considered tri-level. All posted messages are higher priority than user input messages because they reside in different queues. And all user input messages are higher priority than WM_PAINT and WM_TIMER messages.

所以,如果你使用等待Task.Yield()来产生在试图保持UI响应消息循环,你实际上是在阻碍用户界面的风险线程的消息循环。一些悬而未决的用户输入的信息,以及 WM_PAINT WM_TIMER ,具有比继续发布消息的优先级较低。因此,如果你做的等待Task.Yield()在一个紧密的循环,你仍然可以阻止用户界面。

So, if you use await Task.Yield() to yield to the message loop in attempt to keep the UI responsive, you are actually at risk of obstructing the UI thread's message loop. Some pending user input messages, as well as WM_PAINT and WM_TIMER, have a lower priority than the posted continuation message. Thus, if you do await Task.Yield() on a tight loop, you still may block the UI.

这是它是如何从你的问题中提到的JavaScript的的SetTimer 比喻不同。 A 的SetTimer 的回调将被调用的之后的所有用户输入的信息已被浏览器的消息泵处理。

This is how it is different from the JavaScript's setTimer analogy you mentioned in the question. A setTimer callback will be called after all user input message have been processed by the browser's message pump.

因此​​,等待Task.Yield()不是好做的UI线程后台工作。事实上,你很少需要运行在UI线程上的后台进程,但有时你做什么,例如编辑语法高亮,拼写检查等。在这种情况下,使用该框架的空闲基础设施。

So, await Task.Yield() is not good for doing background work on the UI thread. In fact, you very rarely need to run a background process on the UI thread, but sometimes you do, e.g. editor syntax highlighting, spell checking etc. In this case, use the framework's idle infrastructure.

例如,使用WPF你可以做等待Dispatcher.Yield(DispatcherPriority.ApplicationIdle)

E.g., with WPF you could do await Dispatcher.Yield(DispatcherPriority.ApplicationIdle):

async Task DoUIThreadWorkAsync(CancellationToken token)
{
    var i = 0;

    while (true)
    {
        token.ThrowIfCancellationRequested();

        await Dispatcher.Yield(DispatcherPriority.ApplicationIdle);

        // do the UI-related work item
        this.TextBlock.Text = "iteration " + i++;
    }
}

有关的WinForms,你可以使用 Application.Idle 事件:

For WinForms, you could use Application.Idle event:

// await IdleYield();

public static Task IdleYield()
{
    var idleTcs = new TaskCompletionSource<bool>();
    // subscribe to Application.Idle
    EventHandler handler = null;
    handler = (s, e) =>
    {
        Application.Idle -= handler;
        idleTcs.SetResult(true);
    };
    Application.Idle += handler;
    return idleTcs.Task;
}

<一个href=\"http://blogs.msdn.com/b/windowsappdev/archive/2012/03/20/keeping-apps-fast-and-fluid-with-asynchrony-in-the-windows-runtime.aspx\">It建议您不要在UI线程上运行这样的背景下运作的每一次迭代超过50毫秒。

It is recommended that you do not exceed 50ms for each iteration of such background operation running on the UI thread.

对于非UI线程没有同步上下文,等待Task.Yield()只是切换延续随机池线程。谁也不能保证这将是一个的不同的的从当前线程的线程,它唯一的保证是的异步的延续。如果线程池是饿的,它可能会安排到延续同一个线程。

For a non-UI thread with no synchronization context, await Task.Yield() just switches the continuation to a random pool thread. There is no guarantee it is going to be a different thread from the current thread, it's only guaranteed to be an asynchronous continuation. If ThreadPool is starving, it may schedule the continuation onto the same thread.

在ASP.NET ,做等待Task.Yield()没有任何意义可言,除了中提到的解决方法< A HREF =htt​​p://stackoverflow.com/a/23436765/1768303> @ StephenCleary的回答。否则,它只会伤害具有冗余线程切换的web应用程序的性能。

In ASP.NET, doing await Task.Yield() doesn't make sense at all, except for the workaround mentioned in @StephenCleary's answer. Otherwise, it will only hurt the web app performance with a redundant thread switch.

那么,是等待Task.Yield()有用吗? IMO,并不多。它可以作为一个快捷方式运行通过延续 SynchronizationContext.Post ThreadPool.QueueUserWorkItem ,如果你真的需要在你的方法的一部分强加不同步。

So, is await Task.Yield() useful? IMO, not much. It can be used as a shortcut to run the continuation via SynchronizationContext.Post or ThreadPool.QueueUserWorkItem, if you really need to impose asynchrony upon a part of your method.

关于你的书引用,在我看来,这些方法使用 Task.Yield 是错误的。我解释了为什么他们是错误的UI线程,上面。对于非UI线程池,也根本没有的的线程其它任务执行的,除非你运行一个自定义任务泵一样的Stephen Toub的 AsyncPump

Regarding the books you quoted, in my opinion those approaches to using Task.Yield are wrong. I explained why they're wrong for a UI thread, above. For a non-UI pool thread, there's simply no "other tasks in the thread to execute", unless you running a custom task pump like Stephen Toub's AsyncPump.

更新回答您的注释:

...怎么会这样asynchronouse操作,留在同一个线程
  ?..

... how can it be asynchronouse operation and stay in the same thread ?..

作为一个简单的例子:WinForms应用程序:

As a simple example: WinForms app:

async void Form_Load(object s, object e) 
{ 
    await Task.Yield(); 
    MessageBox.Show("Async message!");
}

的Form_Load 将返回给调用者(在WinFroms框架code已经解雇了加载事件)然后将消息框将被异步地 Application.Run中所示,在将来的某个消息循环运行的迭代()。延续回调排队与 WinFormsSynchronizationContext.Post ,在内部岗位私人Windows消息发布到UI线程的消息循环。当这个消息被抽,还是在同一线程上的回调将被执行。

Form_Load will return to the caller (the WinFroms framework code which has fired Load event), and then the message box will be shown asynchronously, upon some future iteration of the message loop run by Application.Run(). The continuation callback is queued with WinFormsSynchronizationContext.Post, which internally posts a private Windows message to the UI thread's message loop. The callback will be executed when this message gets pumped, still on the same thread.

在一个控制台应用程序,可以运行一个类似的序列化循环 AsyncPump 上面提到的。

In a console app, you can run a similar serializing loop with AsyncPump mentioned above.

这篇关于Task.Yield - 真正的用途?的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆