我正在阅读async/await,什么时候Task.Yield可能有用,并且发现了这篇文章. 我从那篇文章中得到了关于下面的问题:
使用async/await时,无法保证在执行await时调用的方法
FooAsync()实际上是异步运行的.内部实现可以使用完全同步的路径自由返回.
这对我来说有点不清楚,可能是因为我头脑中的异步定义没有排成一行.
在我看来,由于我主要做UI开发,异步代码是不在UI线程上运行的代码,而是在其他一些线程上.我想在我引用的文本中,如果一个方法阻塞在任何线程上(即使它是一个线程池线程),它也不是真正的异步.
题:
如果我有一个长时间运行的任务是CPU绑定的(假设它做了很多硬数学运算),那么异步运行该任务必须阻塞一些线程吗?有些东西实际上要做数学.如果我等待它,那么一些线程就会被阻止.
什么是真正的异步方法的例子,它们将如何实际工作?这些是否仅限于利用某些硬件功能的I/O操作,因此没有阻止任何线程?
我一直在阅读有关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.... …Run Code Online (Sandbox Code Playgroud) 简单的搜索DoEvents会带来很多结果,这些结果基本上会导致:
DoEvents是邪恶的.不要使用它.请改用线程.
一般引用的原因是:
但是一些值得注意的Win32功能,例如TrackPopupMenu并DoDragDrop 执行自己的消息处理以保持UI响应,就像这样DoEvents.
然而,这些问题似乎都没有遇到过这些问题(性能,再入等等).
他们是如何做到的呢?他们如何避免引用的问题DoEvents?(或者做他们?)
如果我需要推迟代码执行,直到UI线程消息循环的未来迭代之后,我可以这样做:
await Task.Factory.StartNew(
() => {
MessageBox.Show("Hello!");
},
CancellationToken.None,
TaskCreationOptions.None,
TaskScheduler.FromCurrentSynchronizationContext());
Run Code Online (Sandbox Code Playgroud)
这将类似于await Task.Yield(); MessageBox.Show("Hello!");,除了我有一个选项可以取消任务,如果我想.
在使用默认同步上下文的情况下,我可以类似地使用await Task.Run继续池线程.
事实上,我喜欢Task.Factory.StartNew和Task.Run更多Task.Yield,因为他们都明确定义了延续代码的范围.
那么,在什么情况下await Task.Yield()实际上有用呢?
我今天尝试使用SwitchTo方法切换到GUI线程,并发现我解除它的示例不起作用,仅仅是因为该方法不存在.
然后我在这里发现了这个模糊:
我们摆脱它的原因是因为它太危险了.另一种方法是在TaskEx.Run中捆绑你的代码......
我的问题很简单:为什么它很危险?使用它会带来哪些特定的危险?
请注意,我确实阅读了该帖子的其余部分,因此我确实理解这里存在技术限制.我的问题仍然是,如果我意识到这一点,为什么它很危险?
我正在考虑重新实现帮助方法来给我指定的功能,但如果有一些根本性的破坏,除了有人认为它是危险的,我不会这样做.
具体来说,非常天真,这是我如何考虑实现所需的方法:
public static class ContextSwitcher
{
public static ThreadPoolContextSwitcher SwitchToThreadPool()
{
return new ThreadPoolContextSwitcher();
}
public static SynchronizationContextSwitcher SwitchTo(this SynchronizationContext synchronizationContext)
{
return new SynchronizationContextSwitcher(synchronizationContext);
}
}
public class SynchronizationContextSwitcher : INotifyCompletion
{
private readonly SynchronizationContext _SynchronizationContext;
public SynchronizationContextSwitcher(SynchronizationContext synchronizationContext)
{
_SynchronizationContext = synchronizationContext;
}
public SynchronizationContextSwitcher GetAwaiter()
{
return this;
}
public bool IsCompleted
{
get
{
return false;
}
}
public void OnCompleted(Action action) …Run Code Online (Sandbox Code Playgroud) 它建议使用一个ConfigureAwait(false)当你可以随时,特别是在图书馆,因为它可以帮助避免死锁和提高性能.
我编写了一个大量使用异步的库(访问数据库的Web服务).图书馆的用户遇到了僵局,经过多次痛苦的调试和修补后,我将其追踪到了单独使用await Task.Yield().我使用过的其他任何地方.ConfigureAwait(false),但是不支持Task.Yield().
对于需要等效的情况,推荐的解决方案是Task.Yield().ConfigureAwait(false)什么?
我已经读过有关如何SwitchTo移除某个方法的内容.我可以看出为什么那可能是危险的,但为什么没有相应的Task.Yield().ConfigureAwait(false)?
编辑:
为了提供我的问题的进一步背景,这里有一些代码.我正在实现一个开源库,用于访问支持异步的DynamoDB(作为AWS的服务的分布式数据库).IX-Async库IAsyncEnumerable<T>提供了许多操作返回.该库不提供从以"块"提供行的数据源生成异步枚举的好方法,即每个异步请求返回许多项.所以我有自己的通用类型.该库支持预读选项,允许用户指定在调用实际需要之前应该请求多少数据.MoveNext()
基本上,这是如何工作的,我通过调用GetMore()和传递这些块之间的状态来请求块.我将这些任务放入chunks队列并将它们出列并将它们转换为我放在单独队列中的实际结果.这个NextChunk()方法就是问题所在.根据值的不同,ReadAhead我会在完成最后一个(最多)之后保持获取下一个块,直到需要一个值但不可用(无)或仅获取超出当前使用值的下一个块(一些).因此,获取下一个块应该并行/不阻止获取下一个值.枚举器代码是:
private class ChunkedAsyncEnumerator<TState, TResult> : IAsyncEnumerator<TResult>
{
private readonly ChunkedAsyncEnumerable<TState, TResult> enumerable;
private readonly ConcurrentQueue<Task<TState>> chunks = new ConcurrentQueue<Task<TState>>();
private readonly Queue<TResult> results = new Queue<TResult>();
private CancellationTokenSource cts = new CancellationTokenSource();
private TState lastState;
private TResult current;
private …Run Code Online (Sandbox Code Playgroud) 在 Blazor 中,如何撤消无效的用户输入,而不更改组件的状态以触发重新渲染?
这是一个简单的 Blazor 计数器示例(在线尝试):
<label>Count:</label>
<button @onclick=Increment>@count times!</button><br>
A: <input @oninput=OnChange value="@count"><br>
B: <input @bind-value=count @bind-value:event="oninput">
@code {
int count = 1;
void Increment() => count++;
void OnChange(ChangeEventArgs e)
{
var userValue = e.Value?.ToString();
if (int.TryParse(userValue, out var v))
{
count = v;
}
else
{
if (String.IsNullOrWhiteSpace(userValue))
{
count = 0;
}
// if count hasn't changed here,
// I want to re-render "A"
// this doesn't work
e.Value = count.ToString();
// this doesn't work …Run Code Online (Sandbox Code Playgroud) Mono/WASM 运行时(由 Blazor WebAssembly 使用)的Task.Yield底层如何工作?
澄清一下,我相信我对.NET Framework 和 .NET Core 的工作原理有很好的了解。Task.YieldMono 实现看起来并没有太大不同,简而言之,它可以归结为:
static Task Yield()
{
var tcs = new TaskCompletionSource<bool>();
System.Threading.ThreadPool.QueueUserWorkItem(_ => tcs.TrySetResult(true));
return tcs.Task;
}
Run Code Online (Sandbox Code Playgroud)
令人惊讶的是,这也适用于 Blazor WebAssembly(在线尝试):
<label>Tick Count: @tickCount</label><br>
@code
{
int tickCount = System.Environment.TickCount;
protected override void OnAfterRender(bool firstRender)
{
if (firstRender) CountAsync();
}
static Task Yield()
{
var tcs = new TaskCompletionSource<bool>();
System.Threading.ThreadPool.QueueUserWorkItem(_ => tcs.TrySetResult(true));
return tcs.Task;
}
async void CountAsync()
{
for (var …Run Code Online (Sandbox Code Playgroud) 我一直在制作服务器.我在方法中使用TcpListener.AcceptTcpClientAsync()async,但我不知道如何实际使它工作.我的代码现在是:
private static async void StartServer()
{
Console.WriteLine("S: Server started on port {0}", WebVars.ServerPort);
var listener = new TcpListener(WebVars.LocalIp, WebVars.ServerPort);
listener.Start();
var client = await listener.AcceptTcpClientAsync();
}
Run Code Online (Sandbox Code Playgroud)
我该如何处理客户?我只是继续编码,它会自动创建相同方法的新线程,还是我需要做一些魔术方法来为我做?
编辑:当前代码:
private static Task HandleClientAsync(TcpClient client)
{
var stream = client.GetStream();
// do stuff
}
/// <summary>
/// Method to be used on seperate thread.
/// </summary>
private static async void RunServerAsync()
{
while (true)
{
Console.WriteLine("S: Server started on port {0}", WebVars.ServerPort);
var listener = new TcpListener(WebVars.LocalIp, WebVars.ServerPort);
listener.Start(); …Run Code Online (Sandbox Code Playgroud) 我想我不懂东西.我原本以为Task.Yield()强制为一个任务启动一个新的线程/上下文,但是在重新阅读这个答案时,它似乎只是强制该方法是异步的.它仍然是在相同的背景下.
什么是正确的方法 - 在asp.net进程中 - 并行创建和运行多个任务而不会导致死锁?
换句话说,假设我有以下方法:
async Task createFileFromLongRunningComputation(int input) {
//many levels of async code
}
Run Code Online (Sandbox Code Playgroud)
当某个POST路由被命中时,我想同时启动上述方法3次,立即返回,但是当完成所有这三个时都要记录.
我想我需要把这样的东西放进我的行动中
public IHttpAction Post() {
Task.WhenAll(
createFileFromLongRunningComputation(1),
createFileFromLongRunningComputation(2),
createFileFromLongRunningComputation(3)
).ContinueWith((Task t) =>
logger.Log("Computation completed")
).ConfigureAwait(false);
return Ok();
Run Code Online (Sandbox Code Playgroud)
}
需要了解什么createFileFromLongRunningComputation?我原以为Task.Yield是正确的,但显然不是.
在使用.NET async/await API时,我遇到了好奇心:循环忽略了用作超时的延迟,直到我在循环内添加了一个短暂的延迟.这是如何运作的?不是最直观的行为!
完整计划:
using System;
using System.Threading.Tasks;
public class Program
{
public static void Main(String[] args)
{
Task.Run(async () =>
{
await Task.WhenAny(Loop(), Task.Delay(TimeSpan.FromSeconds(1)));
Console.WriteLine("Timed out!");
})
.Wait();
}
public static async Task Loop()
{
while(true)
{
// Commenting this out makes the code loop indefinitely!
await Task.Delay(TimeSpan.FromMilliseconds(1));
// This doesn't matter.
await DoWork();
}
}
public static async Task DoWork()
{
await Task.CompletedTask;
}
}
Run Code Online (Sandbox Code Playgroud)
背景
实际的程序有,while(!done)但由于一个错误done从未设置为true.循环进行多次await调用.该Task.WhenAny呼叫是在单元测试中以防止Loop() …
鉴于这个例子:
void Main() { Test().Wait(); }
async Task Test()
{
Console.WriteLine("Test A");
await AsyncFunction();
// It doesn't matter where the await is in AsyncFunction, it will always wait for the function to complete before executing the next line.
Console.WriteLine("Test B");
}
async Task AsyncFunction()
{
Console.WriteLine("AsyncFunction A");
await Task.Yield();
Console.WriteLine("AsyncFunction B");
}
Run Code Online (Sandbox Code Playgroud)
在任何情况下都不会在"AsyncFunction B"之前显示"Test B"
Test()中的await语句不是等待Task.Yield()完成恢复,而是整个AsyncFunction完成?
c# ×11
async-await ×8
.net ×6
asynchronous ×4
asp.net ×2
blazor ×2
.net-4.5 ×1
async-ctp ×1
c#-5.0 ×1
doevents ×1
task ×1
tcp ×1
tcpclient ×1
tcplistener ×1
visual-c++ ×1
webassembly ×1
winapi ×1
windows ×1
winforms ×1