我终于研究了async和await关键字,我有点"得到",但我见过的所有例子都在.Net框架中调用异步方法,例如这个调用HttpClient.GetStringAsync().
我不太清楚的是这种方法会发生什么,以及我如何编写自己的"等待"方法.它是否像包装我想在Task中异步运行的代码并返回那样简单?
如果我需要推迟代码执行,直到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) 我试图了解async/await关键字的工作原理.我在WPF窗口上有一个textblock绑定到TextBlockContent字符串属性和一个触发单击ChangeText()的按钮.
这是我的代码:
public async void ChangeText()
{
string mystring = await TestAsync();
this.TextBlockContent= mystring;
}
private async Task<string> TestAsync()
{
var mystring = await GetSomeString().ConfigureAwait(false);
mystring = mystring + "defg";
return mystring;
}
private Task<string> GetSomeString()
{
return Task.Run(() =>
{
return "abc";
});
}
Run Code Online (Sandbox Code Playgroud)
从我的读数中,我了解到ConfigureAwait设置为false将允许我指定我不想保存当前上下文以用于需要在await关键字之后执行的Task的"rest".
在调试之后,我意识到当我有这段代码时,有时代码在主线程之后运行:await GetSomeString().ConfigureAwait(false); 虽然我特意添加了配置等待.
我原以为它总是运行在与进入Task之前的那个线程不同的线程上运行.
有人可以帮我理解为什么吗?
非常感谢你
是否可以强制继续使用async-await语句在自定义ThreadPool的线程上运行?
上下文:我正在运行一个ASP应用程序并在后台做了相当多的工作.我正在通过自编写的ThreadPool完成所有工作,但如果我使用async-await模式,则延续总是在名为"Worker Thread"的线程上运行.我很确定这是来自默认ThreadPool的线程,它也用于处理HTTP请求.由于默认ThreadPool的所有线程都忙着继续我的后台工作,这导致这些请求的饥饿.
尽管已经编写了多年的C#,但我不是async的专家,但是在阅读了一些MSDN博客文章之后,AFAICT:
Task)可以捕获或不捕获当前SynchronizationContext.SynchronizationContext大致相当于一个线程:如果我在UI线程和呼叫上await task,这"流动语境",延续在UI线程上运行.如果我调用await task.ConfigureAwait(false),则继续在一些随机线程池线程上运行,该线程可能是/可能不是UI线程.OnCompleted流上下文,UnsafeOnCompleted并不流动上下文.好了,有了这个,我们来看看Roslyn生成的代码await Task.Yield().这个:
using System;
using System.Threading.Tasks;
public class C {
public async void M() {
await Task.Yield();
}
}
Run Code Online (Sandbox Code Playgroud)
这个编译器生成的代码的结果(你可以在这里验证自己):
public class C
{
[CompilerGenerated]
[StructLayout(LayoutKind.Auto)]
private struct <M>d__0 : IAsyncStateMachine
{
public int <>1__state;
public AsyncVoidMethodBuilder <>t__builder;
private YieldAwaitable.YieldAwaiter <>u__1;
void IAsyncStateMachine.MoveNext()
{
int num = this.<>1__state;
try
{
YieldAwaitable.YieldAwaiter yieldAwaiter;
if (num != 0) …Run Code Online (Sandbox Code Playgroud) 上个月我问了以下问题,这导致我学到了TaskEx.Yield:
但是,我已经意识到这个方法实际上将所有后续代码提交给环境TaskScheduler.在真正的DI精神中,我们的团队同意尽可能避免使用环境实例,所以我想知道是否可以明确指定TaskScheduler使用?
像下面这样的东西会很棒:
public static YieldAwaitable Yield(TaskScheduler taskScheduler)
{
return new YieldAwaitable(taskScheduler);
}
Run Code Online (Sandbox Code Playgroud)
但是,Async CTP的当前实现仅提供:
public static YieldAwaitable Yield()
{
return new YieldAwaitable(SynchronizationContext.Current ?? TaskScheduler.Current);
}
Run Code Online (Sandbox Code Playgroud)
以下是否会提供可接受的有效替代方案?
await Task.Factory.StartNew(() => { }, CancellationToken.None, TaskCreationOptions.None, this.TaskScheduler);
Run Code Online (Sandbox Code Playgroud) 考虑这个Windows窗体代码(可以编写类似的WPF模拟代码):
public partial class Form1 : Form
{
public Form1()
{
InitializeComponent();
}
private void TraceThreadInfo([CallerMemberName]string callerName = null)
{
Trace.WriteLine($"{callerName} is running on UI thread: {!this.InvokeRequired}");
}
private void DoCpuBoundWork([CallerMemberName]string callerName = null)
{
TraceThreadInfo(callerName);
for (var i = 0; i < 1000000000; i++)
{
// do some work here
}
}
private async Task Foo()
{
DoCpuBoundWork();
await Bar();
}
private async Task Bar()
{
DoCpuBoundWork();
await Boo();
}
private async Task Boo()
{
DoCpuBoundWork();
// e.g., saving …Run Code Online (Sandbox Code Playgroud) c# ×8
async-await ×7
.net ×4
async-ctp ×2
asynchronous ×2
wpf ×2
.net-4.5 ×1
task ×1
winforms ×1