Roy*_*mir 38 c# multithreading async-await c#-5.0 .net-4.5
我一直在阅读有关Task.Yield
,而且作为一名JavaScript开发,我可以告诉大家,就是它的工作是 完全相同一样setTimeout(function (){...},0);
在让主单线程处理又名其他的东西方面:
"不要把所有的力量从时间上释放出来 - 所以其他人也会有一些......"
在js中,它特别适用于长循环.(不要让浏览器冻结......)
但我在这里看到了这个例子:
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;
}
Run Code Online (Sandbox Code Playgroud)
作为JS程序员,我可以理解他们在这里做了什么.
但作为一名C#程序员,我问自己:为什么不为它开一个任务呢?
public static async Task < int > FindSeriesSum(int i1)
{
//do something....
return await MyLongCalculationTask();
//do something
}
Run Code Online (Sandbox Code Playgroud)
题
使用Js我无法打开任务(是的,我知道我实际上可以与网络工作者).但是用c#我可以.
如果是这样 - 为什么甚至在我可以释放它的时候不时释放呢?
添加参考:
从这里:
从这里(另一本电子书):
nos*_*tio 60
当你看到:
await Task.Yield();
Run Code Online (Sandbox Code Playgroud)
你可以这样思考:
await Task.Factory.StartNew(
() => {},
CancellationToken.None,
TaskCreationOptions.None,
SynchronizationContext.Current != null?
TaskScheduler.FromCurrentSynchronizationContext():
TaskScheduler.Current);
Run Code Online (Sandbox Code Playgroud)
所有这一切确保了延续将在未来异步发生.通过异步我的意思是执行控件将返回到async
方法的调用者,并且延续回调不会发生在同一堆栈帧上.
确切地说,它将在什么线程上完全取决于调用者线程的同步上下文.
对于UI线程,延续将在消息循环的某个未来迭代中发生,由Application.Run
(WinForms)或Dispatcher.Run
(WPF)运行.在内部,它归结为Win32 PostMessage
API,它将自定义消息发布到UI线程的消息队列.在await
当这个消息被泵送和处理延续回调将被调用.关于何时会发生这种情况,你完全无法控制.
此外,Windows有自己的优先级来抽取消息:INFO:窗口消息优先级.最相关的部分:
在这种方案下,优先级可以被认为是三级的.所有发布的消息都优先于用户输入消息,因为它们位于不同的队列中.并且所有用户输入消息的优先级都高于WM_PAINT和WM_TIMER消息.
因此,如果您await Task.Yield()
在尝试保持UI响应时使用消息循环,则实际上存在阻碍UI线程消息循环的风险.一些待处理的用户输入信息,以及WM_PAINT
和WM_TIMER
,具有比张贴继续消息较低的优先级.因此,如果您await Task.Yield()
在紧密循环中执行操作,您仍可能会阻止UI.
这就是setTimer
你在问题中提到的JavaScript的类比.一个setTimer
回调将被调用后,所有的用户输入信息已被浏览器的消息泵处理.
因此,await Task.Yield()
在UI线程上进行后台工作并不好.实际上,您很少需要在UI线程上运行后台进程,但有时您会这样做,例如编辑器语法突出显示,拼写检查等.在这种情况下,请使用框架的空闲基础结构.
例如,使用WPF,您可以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++;
}
}
Run Code Online (Sandbox Code Playgroud)
对于WinForms,您可以使用Application.Idle
事件:
// 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;
}
Run Code Online (Sandbox Code Playgroud)
对于在UI线程上运行的此类后台操作的每次迭代,建议您不要超过50毫秒.
对于没有同步上下文的非UI线程,await Task.Yield()
只需将continuation切换到随机池线程.不能保证它将与当前线程不同,它只能保证是异步延续.如果ThreadPool
正在挨饿,它可以将延续计划到同一个线程上.
在ASP.NET中,await Task.Yield()
除了@ StephenCleary的答案中提到的解决方法之外,完成任务都没有意义.否则,它只会损害使用冗余线程切换的Web应用程序性能.
那么,await Task.Yield()
有用吗?国际海事组织,并不多.它可以用作运行continuation via的快捷方式,SynchronizationContext.Post
或者ThreadPool.QueueUserWorkItem
,如果你真的需要在方法的一部分上强加异步.
关于你引用的书籍,我认为这些使用方法Task.Yield
是错误的.我在上面解释了为什么他们错误的UI线程.对于非UI池线程,除了运行像Stephen Toub这样的自定义任务泵之外,根本没有"要执行的线程中的其他任务".AsyncPump
更新以回答评论:
...怎样才能进行异步操作并保持在同一个线程中?
举个简单的例子:WinForms app:
async void Form_Load(object s, object e)
{
await Task.Yield();
MessageBox.Show("Async message!");
}
Run Code Online (Sandbox Code Playgroud)
Form_Load
将返回调用者(已触发Load
事件的WinFroms框架代码),然后在消息循环的某个未来迭代运行时异步显示消息框Application.Run()
.延续回调排队WinFormsSynchronizationContext.Post
,在内部将私有Windows消息发布到UI线程的消息循环.当此消息被泵送时,仍将在同一线程上执行回调.
在控制台应用程序中,您可以运行与AsyncPump
上面提到的类似的序列化循环.
不,这与使用setTimeout
将控制权返回到UI完全不同.在Javascript中,总是让UI更新,因为setTimeout
总是有几毫秒的最小暂停,并且挂起的UI工作优先于定时器,但await Task.Yield();
不会这样做.
有没有保证的收益率将让任何工作在主线程来完成,相反调用该收益率将超过经常工作UI优先的代码.
"大多数UI环境中UI线程上存在的同步上下文通常会优先考虑发布到上下文的工作,而不是输入和渲染工作.因此,不要依赖等待Task.Yield();来保持UI响应".
归档时间: |
|
查看次数: |
16955 次 |
最近记录: |