Kru*_*lur 196 c# async-await
我正在使用异步/等待和Task很多,但从来没有使用过Task.Yield(),说实话,即使有所有的解释,我不明白为什么我需要这种方法.
有人可以在Yield()需要的地方举个好例子吗?
Ree*_*sey 211
当您使用async/时await,无法保证您在执行时调用的方法await FooAsync()实际上是异步运行的.内部实现可以使用完全同步的路径自由返回.
如果您正在创建一个API,其中关键是您不阻止并且异步运行某些代码,并且被调用的方法可能会同步运行(有效阻塞),使用await Task.Yield()将强制您的方法异步,并返回那时控制.其余代码将在稍后的时间执行(此时,它仍可以同步运行)在当前上下文中.
如果你创建一个需要一些"长时间运行"初始化的异步方法,这也很有用,即:
private async void button_Click(object sender, EventArgs e)
{
await Task.Yield(); // Make us async right away
var data = ExecuteFooOnUIThread(); // This will run on the UI thread at some point later
await UseDataAsync(data);
}
Run Code Online (Sandbox Code Playgroud)
没有Task.Yield()调用,该方法将一直执行到第一次调用await.
nos*_*tio 29
在内部,await Task.Yield()只需在当前同步上下文或随机池线程上对延续进行排队,如果SynchronizationContext.Current是null.
它被有效地实现为定制awaiter.产生相同效果的效率较低的代码可能就像这样简单:
var tcs = new TaskCompletionSource<bool>();
var sc = SynchronizationContext.Current;
if (sc != null)
sc.Post(_ => tcs.SetResult(true), null);
else
ThreadPool.QueueUserWorkItem(_ => tcs.SetResult(true));
await tcs.Task;
Run Code Online (Sandbox Code Playgroud)
Task.Yield()可以用作一些奇怪的执行流程更改的捷径.例如:
async Task DoDialogAsync()
{
var dialog = new Form();
Func<Task> showAsync = async () =>
{
await Task.Yield();
dialog.ShowDialog();
}
var dialogTask = showAsync();
await Task.Yield();
// now we're on the dialog's nested message loop started by dialog.ShowDialog
MessageBox.Show("The dialog is visible, click OK to close");
dialog.Close();
await dialogTask;
// we're back to the main message loop
}
Run Code Online (Sandbox Code Playgroud)
也就是说,我无法想到任何Task.Yield()无法用Task.Factory.StartNew适当的任务调度程序替换的情况.
也可以看看:
Task.Yield()类似于Thread.Yield()async-await 的对应部分,但具有更具体的条件。您甚至需要多少次Thread.Yield()?我将首先广泛回答标题“您什么时候会使用Task.Yield()”。当满足以下条件时您会:
术语“异步上下文”在这里的意思是“SynchronizationContext先然后TaskScheduler”。它是由史蒂芬·克利里(Stephen Cleary)使用的。
Task.Yield()大约是这样做的(许多帖子在这里和那里都略有错误):
await Task.Factory.StartNew(
() => {},
CancellationToken.None,
TaskCreationOptions.PreferFairness,
SynchronizationContext.Current != null?
TaskScheduler.FromCurrentSynchronizationContext():
TaskScheduler.Current);
Run Code Online (Sandbox Code Playgroud)
如果任何一个条件被破坏,则需要使用其他替代方案。
如果任务应该继续Task.DefaultScheduler,您通常会使用ConfigureAwait(false). 相反,Task.Yield()给你一个等待的没有ConfigureAwait(bool)。您需要将近似代码与 一起使用TaskScheduler.Default。
如果Task.Yield()阻塞队列,您需要按照nosratio的解释重新构建代码。
如果您需要在更晚的时间(例如,以毫秒为单位)继续发生,您可以使用Task.Delay.
如果您希望任务在队列中可取消,但不想检查取消标记,也不想自己引发异常,则需要使用带有取消标记的近似代码。
Task.Yield()是如此利基,很容易被躲避。我只是结合我的经验想象出一个例子。它是为了解决受自定义调度程序约束的异步哲学家就餐问题。在我的多线程帮助程序库InSync中,它支持异步锁的无序获取。如果当前的异步获取失败,它会将异步获取排入队列。代码在这里。它需要ConfigureAwait(false)作为通用库,所以我需要使用Task.Factory.StartNew. 在闭源项目中,我的程序需要执行与异步代码混合的重要同步代码
因此,我需要一个自定义调度程序。我可以很容易地想象一些贫穷的开发人员需要以某种方式将同步和异步代码与平行宇宙中的一些特殊调度程序混合在一起(一个宇宙可能不包含这样的开发人员);但为什么他们不直接使用更强大的近似代码,这样他们就不需要编写冗长的注释来解释其原因和作用?
一种用途Task.Yield()是在执行异步递归时防止堆栈溢出。Task.Yield()防止同步延续。但是请注意,这可能会导致 OutOfMemory 异常(如 Triynko 所述)。无限递归仍然不安全,您最好将递归重写为循环。
private static void Main()
{
RecursiveMethod().Wait();
}
private static async Task RecursiveMethod()
{
await Task.Delay(1);
//await Task.Yield(); // Uncomment this line to prevent stackoverlfow.
await RecursiveMethod();
}
Run Code Online (Sandbox Code Playgroud)
| 归档时间: |
|
| 查看次数: |
48220 次 |
| 最近记录: |