有人可以告诉我何时使用a Dispatcher以及何时使用该SynchronizationContext课程?
有一段时间我一直在使用Dispatcher从后台线程排队任务,然后我发现了SynchronizationContext.
我正在使用SynchronizationContext将事件编组回我的DLL中的UI线程,该线程执行许多多线程后台任务.
我知道单例模式不是最喜欢的,但是我现在用它来存储创建foo的父对象时UI的SynchronizationContext的引用.
public class Foo
{
public event EventHandler FooDoDoneEvent;
public void DoFoo()
{
//stuff
OnFooDoDone();
}
private void OnFooDoDone()
{
if (FooDoDoneEvent != null)
{
if (TheUISync.Instance.UISync != SynchronizationContext.Current)
{
TheUISync.Instance.UISync.Post(delegate { OnFooDoDone(); }, null);
}
else
{
FooDoDoneEvent(this, new EventArgs());
}
}
}
}
Run Code Online (Sandbox Code Playgroud)
这在WPF中根本不起作用,TheUISync实例UI同步(从主窗口馈送)永远不会与当前的SynchronizationContext.Current匹配.在Windows窗体中,当我做同样的事情时,他们将在调用后匹配,我们将回到正确的线程.
我讨厌的修复,看起来像
public class Foo
{
public event EventHandler FooDoDoneEvent;
public void DoFoo()
{
//stuff
OnFooDoDone(false);
}
private void OnFooDoDone(bool invoked)
{
if (FooDoDoneEvent != null)
{
if ((TheUISync.Instance.UISync != SynchronizationContext.Current) && (!invoked))
{ …Run Code Online (Sandbox Code Playgroud) 我想先把代码放在一边,然后解释一下情况并根据这个问题提出我的问题:
public partial class MainWindow : Window {
public MainWindow() {
InitializeComponent();
}
private async void Button_Click_2(object sender, RoutedEventArgs e) {
var result = await GetValuesAsync();
Foo.Text += result;
}
public async Task<string> GetValuesAsync() {
using (var httpClient = new HttpClient()) {
var response = await httpClient
.GetAsync("http://www.google.com")
.ConfigureAwait(continueOnCapturedContext: false);
// This is the continuation for the httpClient.GetAsync method.
// We shouldn't get back to sync context here
// Cuz the continueOnCapturedContext is set to *false*
// for the Task …Run Code Online (Sandbox Code Playgroud) c# asynchronous synchronizationcontext task-parallel-library async-await
我正在编写一个多人游戏服务器,我正在研究新的C#async/await功能可以帮助我的方法.服务器的核心是一个循环,它尽可能快地更新游戏中的所有演员:
while (!shutdown)
{
foreach (var actor in actors)
actor.Update();
// Send and receive pending network messages
// Various other system maintenance
}
Run Code Online (Sandbox Code Playgroud)
这个循环需要处理数千个演员并每秒更新多次以保持游戏顺利运行.有些演员偶尔会在更新函数中执行缓慢的任务,例如从数据库中获取数据,这是我想要使用异步的地方.一旦检索到该数据,actor就想要更新游戏状态,这必须在主线程上完成.
由于这是一个控制台应用程序,我计划编写一个SynchronizationContext,它可以将挂起的委托分派给主循环.这允许这些任务在完成后更新游戏,并允许将未处理的异常抛入主循环.我的问题是,如何编写异步更新功能?这非常好用,但打破了不使用async void的建议:
Thing foo;
public override void Update()
{
foo.DoThings();
if (someCondition) {
UpdateAsync();
}
}
async void UpdateAsync()
{
// Get data, but let the server continue in the mean time
var newFoo = await GetFooFromDatabase();
// Now back on the main thread, update game state
this.foo = newFoo;
}
Run Code Online (Sandbox Code Playgroud)
我可以使Update()异步并将任务传播回主循环,但是:
我怎么办这些我无法等待的任务?我唯一想知道他们已经完成的时间是关闭服务器的时候,但是我不想收集可能需要数星期更新的每一项任务.
c# asynchronous synchronizationcontext task-parallel-library async-await
我刚刚注意到,使用.NET 4.5,每个Dispatcher.BeginInvoke/ InvokeAsync回调都是在它自己非常独特的同步上下文(一个实例DispatcherSynchronizationContext)上执行的.这种变化背后的原因是什么?
以下简单的WPF应用程序说明了这一点:
using System;
using System.Diagnostics;
using System.Threading;
using System.Windows;
using System.Windows.Threading;
namespace WpfApplication
{
public partial class MainWindow : Window
{
public MainWindow()
{
InitializeComponent();
Action test = null;
var i = 0;
test = () =>
{
var sc = SynchronizationContext.Current;
Dispatcher.CurrentDispatcher.InvokeAsync(() =>
{
Debug.Print("same context #" + i + ": " +
(sc == SynchronizationContext.Current));
if ( i < 10 )
{
i++;
test();
}
});
};
this.Loaded += …Run Code Online (Sandbox Code Playgroud) 首先请注意,此问题未标记为winforms或wpf或其他任何GUI特定的.这是故意的,你很快就会看到.
第二,对不起,如果这个问题有点长.我尝试将各种各样的信息汇集在一起,以便提供有价值的信息.然而,我的问题正好在"我想知道的事情"之下.
我的任务是最终了解.NET提供的在特定线程上调用委托的各种方法.
我正在寻找最通用的方法(不是Winforms或WPF特定的)来调用特定线程上的委托.
或者,换句话说:我会感兴趣的是,以及如何做各种各样的方式(例如通过WPF Dispatcher)相互利用; 也就是说,如果所有其他人都使用了一种用于跨线程委托调用的通用机制.
有很多与此主题相关的课程; 其中:
SynchronizationContext (in System.Threading)
如果我不得不猜测,那将是最基本的一个; 虽然我不明白它到底是做什么,也不知道它是如何使用的.
AsyncOperation&(in )
这些似乎是包装.不知道如何使用它们.AsyncOperationManager System.ComponentModelSynchronizationContext
WindowsFormsSynchronizationContext (in System.Windows.Forms)
的子类SynchronizationContext.
ISynchronizeInvoke (in System.ComponentModel)
由Windows窗体使用.(Control该类实现了这个.如果我不得不猜测,我会说这个实现可以使用WindowsFormsSynchronizationContext.)
Dispatcher&(in )
似乎后者是另一个子类,前者代表它.DispatcherSynchronizationContext System.Windows.ThreadingSynchronizationContext
有些线程有自己的消息循环,还有消息队列.
(MSDN页面关于消息和消息队列有一些介绍消息循环如何在系统级工作的背景信息,即消息队列作为Windows API.)
我可以看到如何为具有消息队列的线程实现跨线程调用.使用Windows API,您可以将消息放入特定线程的消息队列PostThreadMessage,其中包含调用某个委托的指令.消息循环 - 在该线程上运行 - 最终将到达该消息,并且将调用该委托.
根据我在MSDN上阅读的内容,线程不会自动拥有自己的消息队列.消息队列将变为可用,例如当线程创建窗口时.没有消息队列,线程没有消息循环是没有意义的.
那么,当目标线程没有消息循环时,是否可以进行跨线程委托调用?比方说,在.NET控制台应用程序中?(从这个问题的答案来看,我认为控制台应用确实不可能.)
.net winapi multithreading system.componentmodel synchronizationcontext
我正在学习这门SynchronizationContext课程.我试图了解SynchronizationContext.SetSynchronizationContext()在WinForm/WPF应用程序的上下文中调用的常见用法场景是什么.设置SynchronizationContext线程是什么意思?我什么时候该做,为什么?另外,如果我设置它,我应该在某个时候取消它吗?
编辑:
在他的回答中,@ Hans Passant问我为什么在考虑SetSynchronizationContext().我的想法是在工作线程上设置上下文,以便在该线程上运行的代码将具有要使用的上下文.
private void button3_Click(object sender, EventArgs e)
{
var syncContext = SynchronizationContext.Current;
Task.Factory.StartNew(() =>
{
// Setup the SynchronizationContext on this thread so
// that SomeAsyncComponentThatNeedsACurrentContext
// will have a context when it needs one
if (SynchronizationContext.Current == null)
SynchronizationContext.SetSynchronizationContext(syncContext);
var c = new SomeAsyncComponentThatNeedsACurrentContext();
c.DoSomething();
});
}
Run Code Online (Sandbox Code Playgroud) 如何比较SynchronizationContext?在使用BeginInvoke时,似乎同一个Dispatcher可以创建不同的SynchronizationContext.当我深入研究两个(不相等的)上下文时,我看到调度程序的线程ID是相同的,但它们彼此不相等.
public partial class MainWindow : Window
{
private SynchronizationContext contexta;
private SynchronizationContext contextb;
private SynchronizationContext contextc;
private SynchronizationContext contextd;
public MainWindow()
{
InitializeComponent();
contexta = SynchronizationContext.Current;
Loaded += MainWindow_Loaded;
}
private void MainWindow_Loaded(object sender, RoutedEventArgs e)
{
contextb = SynchronizationContext.Current;
Dispatcher.Invoke(() =>
{
contextc = SynchronizationContext.Current;
});
Dispatcher.BeginInvoke(new Action(() =>
{
contextd = SynchronizationContext.Current;
}));
Debug.Assert(contexta != contextb);
Debug.Assert(contexta == contextc); // fails... why?!?!?
Debug.Assert(contexta == contextd); // fails... why?!?!?
Debug.Assert(contextc == contextd); // fails... why?!?!?
}
Run Code Online (Sandbox Code Playgroud)
也许这两个人不能一起使用.我注意到这实际上有效:
contexta.Send(new …Run Code Online (Sandbox Code Playgroud) 通常建议在ConfigureAwait(false)不需要上下文时等待异步调用时使用。只是想知道ConfigureAwait(false)在 Azure Functions中使用有什么好处。
Azure 函数线程是否具有非空值SynchronizationContext,因此使用ConfigureAwait(false)它可以避免不必要地捕获它并将等待继续重新安排回捕获的SynchronizationContext?
ConfigureAwait(false)在每次异步调用结束时添加有点麻烦,因此如果没有性能/或任何其他相关增益,则最好避免在 Azure Functions 中运行的代码中使用它。
查看azure函数主机代码:https : //github.com/Azure/azure-functions-host/blob/918b057707acfb842659c9dad3cef0193fae1330/src/WebJobs.Script.WebHost/WebScriptHostManager.cs#L181
似乎 azure 函数主机试图在调用 azure 函数之前去除 ASP.NET SynchronizationContext。
synchronizationcontext async-await azure-functions azure-functions-runtime azure-function-async
据我所知,AspNetCore没有 SynchronizationContext.
“重新进入”请求上下文涉及许多内务任务,例如设置 HttpContext.Current 和当前线程的身份和文化。
所以我创建了一个简单的 .Net Core Api 项目,并执行以下操作:
[HttpGet]
[Route("checkVar")]
public async Task<IActionResult> checkVar()
{
Thread.SetData(Thread.GetNamedDataSlot("Random"),4);
await Task.Delay(1000);
var res = Thread.GetData(Thread.GetNamedDataSlot("Random"));
}
Run Code Online (Sandbox Code Playgroud)
令我惊讶的是,res它的价值为4. 我很惊讶,因为我相信这SetData是同步上下文的一部分。(在asp.net core中不应该存在)
更多,当我使用时ConfigureAwait(false),我进入null了res。
所以现在我很困惑。因为ConfigureAwait 在asp.net core中不应该有影响
题:
如果 asp.net core 没有 SynchronizationContext,那么为什么在4之后可用await?为什么会ConfigureAwait在非 SynchronizationContext 环境中改变结果?
c# ×6
async-await ×4
wpf ×4
.net ×3
asynchronous ×2
asp.net-core ×1
dispatcher ×1
winapi ×1
winforms ×1