任务并行库很棒,在过去的几个月里我经常使用它.但是,有一些事情让我感到困扰:事实TaskScheduler.Current是默认的任务调度程序,而不是TaskScheduler.Default.这在文档和样本中乍一看绝对不是很明显.
Current可以导致细微的错误,因为它的行为正在改变,这取决于你是否在另一个任务中.哪个不容易确定.
假设我正在编写异步方法库,使用基于事件的标准异步模式来表示原始同步上下文的完成,这与XxxAsync方法在.NET Framework中完全相同(例如DownloadFileAsync).我决定使用任务并行库来实现,因为使用以下代码实现此行为非常容易:
public class MyLibrary
{
public event EventHandler SomeOperationCompleted;
private void OnSomeOperationCompleted()
{
SomeOperationCompleted?.Invoke(this, EventArgs.Empty);
}
public void DoSomeOperationAsync()
{
Task.Factory.StartNew(() =>
{
Thread.Sleep(1000); // simulate a long operation
}, CancellationToken.None, TaskCreationOptions.None, TaskScheduler.Default)
.ContinueWith(t =>
{
OnSomeOperationCompleted(); // trigger the event
}, TaskScheduler.FromCurrentSynchronizationContext());
}
}
Run Code Online (Sandbox Code Playgroud)
到目前为止,一切运作良好.现在,让我们在WPF或WinForms应用程序中单击按钮调用此库:
private void Button_OnClick(object sender, EventArgs args)
{
var myLibrary = new MyLibrary();
myLibrary.SomeOperationCompleted += (s, e) => DoSomethingElse();
myLibrary.DoSomeOperationAsync(); // call that triggers the …Run Code Online (Sandbox Code Playgroud) .net c# conceptual synchronizationcontext task-parallel-library
请考虑以下Windows窗体代码:
private async void UpdateUIControlClicked(object sender, EventArgs e)
{
this.txtUIControl.Text = "I will be updated after 2nd await - i hope!";
await Task.Delay(5000).ConfigureAwait(continueOnCapturedContext: false);
this.txtUIControl.Text = "I am updated now.";
}
Run Code Online (Sandbox Code Playgroud)
这里异常是在第3行引发的,因为在等待代码在非UI线程上执行之后.ConfigureAwait(false)有用吗?
ConfigureAwait(false)在C#中使用await/async 时,有很多指南可供使用.
似乎一般的建议是ConfigureAwait(false)在库代码中使用,因为它很少依赖于同步上下文.
但是,假设我们正在编写一些非常通用的实用程序代码,它将函数作为输入.一个简单的例子可能是以下(不完整的)功能组合器,以简化基于任务的简单操作:
地图:
public static async Task<TResult> Map<T, TResult>(this Task<T> task, Func<T, TResult> mapping)
{
return mapping(await task);
}
Run Code Online (Sandbox Code Playgroud)
FlatMap:
public static async Task<TResult> FlatMap<T, TResult>(this Task<T> task, Func<T, Task<TResult>> mapping)
{
return await mapping(await task);
}
Run Code Online (Sandbox Code Playgroud)
问题是,我们应该ConfigureAwait(false)在这种情况下使用吗?我不确定上下文捕获是如何工作的.关闭.
一方面,如果组合器以功能方式使用,则不需要同步上下文.另一方面,人们可能会滥用API,并在提供的函数中执行依赖于上下文的内容.
一种选择是为每个场景(Map和/ MapWithContextCapture或某些东西)设置单独的方法,但感觉很难看.
另一种选择可能是将map/flatmap选项添加到a中ConfiguredTaskAwaitable<T>,但是由于等待不必实现接口,这会导致大量冗余代码,在我看来更糟糕.
是否有一种将责任转交给调用者的好方法,这样实现的库就不需要对提供的映射函数中是否需要上下文做出任何假设?
或者仅仅是一个事实,异步方法组成得不是很好,没有各种假设?
只是为了澄清一些事情:
ConfigureAwait(false)将导致空同步.上下文.正如一些答案所提到的那样,可以在方法中添加一个bool-flag,但正如我所看到的,这也不是太漂亮,因为它必须一直传播到API中(因为它有)更多"实用"功能,取决于上面显示的功能.
c# task synchronizationcontext task-parallel-library async-await
感谢Jeremy Miller在日常.NET开发的功能编程方面所做的出色工作,我有一个工作的命令执行器,它可以完成我想要的所有工作(对线程池进行繁重的工作,将结果或错误发送回同步环境,甚至发布进度回同步上下文),但我无法解释为什么它使用SynchronizationContext.Send从线程池和Synchronization.Post从Func传递到了做繁重的方法.我已经多次阅读过这些文档,但我对于它的不同之处并不是很有道理.我应该从一个叫做Send一个叫做一个被调用的事实中获得什么Post?我觉得魔术是Send"启动同步请求"和Post"启动异步请求" 这一事实,但这两个请求都来自线程池,需要发送/发回到UI线程.
有人可以解释这个区别,即使它只是一个助记符设备,让我知道何时选择一个而不是另一个?
在如此重要的情况下,这是我的测试代码,我用Post发进度回UI:
private Action _ExecuteCommand
(SynchronizationContext context
, Action<int, int> progress
, Action<int, int> after)
{
int count = 3;
int accumulatedValue = 0;
int threadId = Thread.CurrentThread.ManagedThreadId;
for (int i = 0; i < count; i++)
{
Thread.Sleep(1000);
context.Post(delegate { progress(i + 1, threadId); });
accumulatedValue += i;
}
return () => …Run Code Online (Sandbox Code Playgroud) c# multithreading conceptual synchronizationcontext threadpool
我还在学习整个任务概念和TPL.根据我目前的理解,SynchronizationContext函数(如果存在)用于await在某处调度任务.另一方面,Task类中的函数不使用上下文,对吧?
因此,例如,Task.Run(...)总是在线程池的工作线程上调度操作并SynchronizationContext.Current完全忽略.await Foobar()将使用上下文执行生成的任务后await?
如果这是真的,我的问题是:我怎样才能获得a Task,实际运行一个动作但是被派遣使用SynchronizationContext.Current.Send/Post?
任何人都可以推荐一个很好的介绍SynchronizationContext,特别是在框架的其余部分使用它们的时间和方式?在MSDN似乎很沉默的类.顶级Google点击(此处和此处)似乎仅适用于Windows Forms调度.Stephen Cleary撰写了一篇文章,很好地了解了已经存在的背景以及它们是如何工作的,但我不了解实际使用的地点和时间.
让我们看看一些简单的C#async/await代码,其中我obj在awaitwith 之前和之后有一个对象引用()ConfigureAwait(false)
private async Task<SomeObject> AnAsyncLibraryMethod(SomeObject obj)
{
Console.WriteLine(Thread.CurrentThread.ManagedThreadId);
obj.Name = "Harry"; // <-- obj here
// MAIN POINT
var newSubObj = await FetchOverHttpAsync().ConfigureAwait(false);
// Continuation here
Console.WriteLine(Thread.CurrentThread.ManagedThreadId);
obj.Name = "Sally"; // <-- same obj here
return obj;
}
public class SomeObject { public string Name; }
Run Code Online (Sandbox Code Playgroud)
ConfigureAwait(false)似乎意味着没有 将延续回到原始上下文 - 好吧,但这究竟意味着什么?我已经尝试了上面的代码并且obj IS正确引用了(即使它在不同的线程上恢复).
所以"上下文"似乎不是线程的工作内存(即线程本地存储).那么"上下文"包含什么?因此,它到底意味着什么
将继续编组回到捕获的原始上下文
我一直在试图追查了以下问题的WinForms应用程序:
本SynchronizationContext.Current是一个任务的延续(即空.ContinueWith这是主要的线程上运行)(我预计当前同步上下文是System.Windows.Forms.WindowsFormsSynchronizationContext).
以下是演示此问题的Winforms代码:
using System;
using System.Threading;
using System.Threading.Tasks;
using System.Windows.Forms;
namespace WindowsFormsApplication1
{
public partial class Form1 : Form
{
public Form1()
{
InitializeComponent();
TaskScheduler ts = TaskScheduler.FromCurrentSynchronizationContext(); // Get the UI task scheduler
// This line is required to see the issue (Removing this causes the problem to go away), since it changes the codeflow in
// \SymbolCache\src\source\.NET\4\DEVDIV_TFS\Dev10\Releases\RTMRel\ndp\clr\src\BCL\System\Threading\ExecutionContext.cs\1305376\ExecutionContext.cs
// at line 435
System.Diagnostics.Trace.CorrelationManager.StartLogicalOperation("LogicalOperation");
var task = Task.Factory.StartNew(() => { });
var cont …Run Code Online (Sandbox Code Playgroud) 我刚写了这段代码:
System.Threading.SynchronizationContext.Current.Post(
state => DoUpdateInUIThread((Abc)state),
abc);
Run Code Online (Sandbox Code Playgroud)
但System.Threading.SynchronizationContext.Current为null
我有几个ASP.Net MVC和WebAPI项目.其中大多数都是最新的(MVC 5/WebAPI 2).我一直在仔细检查我的安全假设,因为我正在实现一个全局过滤器(用于MVC)和一个委托处理程序(用于WebAPI)来统一整个系统的安全性.
在这种情况下,我遇到了一些文章和帖子(见下文),其中说你应该总是设置UseTaskFriendlySynchronizationContext为true(默认为false).这对我来说似乎很奇怪,因为即使在VS2013中使用MVC 5和WebAPI 2新项目模板(以及ASP.Net WebForms模板)也根本不设置此应用程序设置.
关于此设置的MSDN文档几乎不存在,我发现的帖子确实说它是异步编程所必需的,似乎是在WebForms的上下文中.
所以这是我的问题:
以下是我见过的一些文章UseTaskFriendlySynchronizationContext:
SynchronizationContext由Marcus van Houdt 理解ASP.NET中的内容一些文章真正帮助我掌握了所有这些东西的作用,从未提及UseTaskFriendlySynchronizationContext:
asp.net asp.net-mvc synchronizationcontext async-await asp.net-web-api
有人可以告诉我何时使用a Dispatcher以及何时使用该SynchronizationContext课程?
有一段时间我一直在使用Dispatcher从后台线程排队任务,然后我发现了SynchronizationContext.
c# ×8
async-await ×4
conceptual ×2
winforms ×2
.net ×1
asp.net ×1
asp.net-mvc ×1
asynchronous ×1
dispatcher ×1
task ×1
threadpool ×1
wpf ×1