我一直在寻找这个问题的答案,但似乎找不到令人满意的答案。也许有人可以启发我。
我有一个后代,BindingList<T>用于存储对SynchronizationContext对象的引用,以在UI线程上引发其更改的事件。
现在,也有可能BindingList<T>在UI线程而不是后台线程上创建和使用它。没有InvokeRequired可用的财产该如何检查?调用SynchronizationContext.SendUI线程会有什么后果?
c# multithreading invokerequired winforms synchronizationcontext
考虑附加到 Windows 窗体上的按钮的以下演示代码:
private void button1_Click(object sender, System.EventArgs e)
{
var semaphore = new SemaphoreSlim(0, 1);
Invalidate(); // <-- posts a message that surprisingly will be processed while we're waiting
Paint += onPaint;
semaphore.Wait(1000);
Paint -= onPaint;
void onPaint(object s, PaintEventArgs pe)
{
throw new System.NotImplementedException(); // we WILL hit this!
}
}
Run Code Online (Sandbox Code Playgroud)
尽管我们挂在 UI 线程上的信号量等待上,但是Invalidate()当我们挂在Wait()UI 线程上的- AND(当然)上时,仍然会执行由 发布的绘制消息。
这说明了我试图为其创建失败的单元测试的错误的根本原因 - 不使用任何 Windows 窗体。我已经使用 custom SyncronizationContexts 和TaskSchedulers玩了几个小时,但我无法在同一线程上实现这一点。
我想用伪代码做的是:
[Test] …Run Code Online (Sandbox Code Playgroud) 我有一个使用COM组件的C#应用程序.此COM组件需要消息泵(Application.Run())来执行其处理.这意味着它一直停留在主线程上.但是我最近发现可以在另一个获得自己的ApplicationContext的线程上启动另一个Application.Run.
所以我想在其自己的Application.Run()中将COM组件托管在自己的线程上,但我无法弄清楚如何在不创建UI表单的情况下在新线程上启动.
我需要与线程通信的WindowsFormsSynchronizationContext才会在Application.Run()之前创建.但是一旦调用了Application.Run(),我就无法弄清楚如何获得SynchronizationContext.如果我可以在该线程上引发单个事件,我可以使用它来引导整个事物(创建COM对象等),但似乎没有任何地方可以挂钩到没有表单的新事件循环.
我尝试了各种复杂的东西,比如安装一个消息过滤器(在新线程上没有引发消息),将执行上下文复制到另一个线程并尝试从那里检索SynchronizationContext(它拒绝复制一个ExecutionContext已经运行线程),在启动Application.Run()之前检索Thread.CurrentContext,然后调用DoCallbBack()(DoCallback最终在原始线程上),等等.我没有尝试过.
[编辑]改写和简化整个帖子[/编辑]
在这篇博客中,以下(我简化了一点)给出了使用 SynchronizationContext 对象在 UI 线程上运行 Task 的示例:
Task.Factory.StartNew(() =>"Hello World").ContinueWith(
task => textBox1.Text = task.Result,
TaskScheduler.FromCurrentSynchronizationContext());
Run Code Online (Sandbox Code Playgroud)
我可以在一个新项目中重复这些结果,安全地更新 UI,但无论出于何种原因在我当前的项目中(即使它一直在工作)我都不能。我收到标准的“不允许从错误的线程更新 UI”异常。
我的代码(在 MainForm_Load(...) 中)是这样的,它在一个新的项目中工作,在主窗体中添加了一个 textBox1,但在我当前的项目中不起作用:
var one = Task.Factory.StartNew(
() => "Hello, my name is Inigo Montoya");
var two = one.ContinueWith(
task => textBox1.Text = one.Result,
TaskScheduler.FromCurrentSynchronizationContext());
Run Code Online (Sandbox Code Playgroud)
任何人都对可能发生的事情有任何想法。
[编辑]
我已经将错误追溯到使用表单提示用户输入登录信息的对象的实例化。该错误仅在显示表单时发生。(如果我在该表单Show发生之前返回一个硬编码值,则整个事情都可以正常工作)。
新问题:如果我正在构造的表单自己的构造函数在显示之前显示另一个表单,我如何获取它的 SynchronizationContext?您可以通过以下方式重现正在发生的事情:
1) 创建两个表单:带有 a 的 Form1 和带有 a 的TextBoxForm2Button
2)创建一个类 OwnedBy1Uses2
Form1:
public partial class Form1 : Form …Run Code Online (Sandbox Code Playgroud) await 不保证在生成任务的同一任务上继续:
private void TestButton_Click(object sender, RoutedEventArgs e)
{
Task.Run(async () =>
{
Debug.WriteLine("running on task " + Task.CurrentId);
await Task.Delay(TimeSpan.FromMilliseconds(100));
Debug.WriteLine("running on task " + Task.CurrentId);
});
}
Run Code Online (Sandbox Code Playgroud)
这个输出是:
running on task 1
running on task
Run Code Online (Sandbox Code Playgroud)
所以我们可以看到,不仅执行已经移动到另一个任务,而且还移动到UI线程.如何创建专用任务,并强制等待始终继续执行此任务?长时间运行的任务也不会这样做.
我已经看过几个SynchronizationContext实现,但到目前为止它们都没有工作,在这种情况下因为它使用线程而System.Threading.Thread不适用于uwp.
c# asynchronous synchronizationcontext task-parallel-library
我有以下代码:
[TestMethod]
public void StartWorkInFirstThread()
{
if (SynchronizationContext.Current == null)
SynchronizationContext.SetSynchronizationContext(
new SynchronizationContext());
var syncContext = SynchronizationContext.Current;
Console.WriteLine("Start work in the first thread ({0})",
Thread.CurrentThread.ManagedThreadId);
var action = ((Action) DoSomethingInSecondThread);
action.BeginInvoke(CallbackInSecondThread, syncContext);
// Continue its own work
}
private static void DoSomethingInSecondThread()
{
Console.WriteLine("Do something in the second thread ({0})",
Thread.CurrentThread.ManagedThreadId);
}
private void CallbackInSecondThread(IAsyncResult ar)
{
Console.WriteLine("Callback in the second thread ({0})",
Thread.CurrentThread.ManagedThreadId);
var syncContext = (SynchronizationContext) ar.AsyncState;
syncContext.Post(CallbackInFirstThread, null);
}
private void CallbackInFirstThread(object obj)
{
Console.WriteLine("Callback in the …Run Code Online (Sandbox Code Playgroud) 在开发winform应用程序时,通常只需调用以获取主GUI线程来完成GUI工作.
Invoke今天已经过时了(如果我读的正确),相反你应该使用SynchronizationContext相反的.
问题是:我如何处理异常?我注意到有时在"同步/调用"线程上抛出的异常会丢失?
我确实使用了Application.ThreadException,AppDomain.CurrentDomain.UnhandledException但这没有用?
我正在尝试对使用 Prism 事件聚合器的应用程序中的某些行为进行单元测试。我尝试进行单元测试的代码所做的一件事是订阅 UI 线程上的事件。深入研究EventAggregator 的实现,我发现它是通过SynchronizationContext.Post.
我认为这个答案可能是一个很好的解决方法,但我最终使用了一个更简单的修复方法:在单元测试开始时显式设置同步上下文 - 在您尝试阅读之前一直有效SynchronizationContext.Current
这导致我的行为我并不完全理解:
//set the sync context
var thisSyncContext = new SynchronizationContext();
SynchronizationContext.SetSynchronizationContext(thisSyncContext);
thisSyncContext.Post(cb => {
var ctx = SynchronizationContext.Current; //<-- this is null
var equals = thisSyncContext.Equals(ctx); //<-- this is false
},null);
thisSyncContext.Send(cb => {
var ctx = SynchronizationContext.Current; //<-- this is not null
var equals = thisSyncContext.Equals(ctx); //<-- this is true
}, null);
Run Code Online (Sandbox Code Playgroud)
我知道 Post 是异步发生的,Send 是同步发生的,当我在线程调试窗口中观察它时,它实际上会切换到不同的线程 ID,正如您期望的异步调用那样。
我想我想理解的是,当我告诉同步上下文执行一个函数时,无论是同步还是异步,我都希望保留该上下文。它保留用于同步调用,但不适用于异步调用。
为什么会出现这种行为,我该如何在单元测试中补偿它?
我正在创建一个STA版本的SynchronizationContext,以便在Windows Workflow 4.0中使用.我想知道在发布回调时如何处理异常.
所述的SynchronizationContext可用于发送(执行同步地)或邮政(异步执行)类型的代表SendOrPostCallback.虽然在这两种情况下我都在STA线程上调用委托,但是在同步执行时很容易知道如何处理异常.我阻塞调用线程,在我的工作线程上调用回调,记录任何异常,解锁调用线程,并在调用线程上抛出任何记录的异常.
我应该在异步Post上做什么不太清楚.没有机制将该异常从执行线程转移回调用线程; 邮差 100%火,忘了.没有EndInvoke会()或WaitHandle的在 SendOrPostCallback.抛出的任何异常都将导致应用程序被拆除.
我别无选择,只能在帖子中抛出异常,推倒我的申请表吗?这似乎是框架中SynchronizationContexts的默认行为(谢谢,Reflector).我似乎无法弄清楚为什么会这样.难道不应该有一些方法来阻止异步Post s的繁荣吗?
.net c# workflow-foundation synchronizationcontext workflow-foundation-4
阅读短语"当前的SynchronizationContext是当前线程的属性"更正",我有点困惑......
在VS2010中的C#应用程序代码中,当我输入时,Thread.CurrentThread.我没有在Intellisense给出的选项的下拉列表中找到线程的任何上下文相关属性.
我知道当前的同步上下文可以通过" = SynchronizationContext.Current;"获得.但是,并行线程,任务等同时执行并不是很幸运.
假设从一个控制台或WPF (*)应用程序,我在自己的主UI线程和TPL任务中创建并启动一些Windows窗体.
我认为每个winform都应该有自己的WindowsFormaSynchronizationContext,WPF应该有自己的DispatcherSynchronizationContext(SynchronizationContext类的子类)实例,任务在ThreadPool中用自己的同步上下文 执行,LongRunning任务也可以在其中执行线程池.拥有同步上下文......
那么,为什么不能SynchronizationContext从线程中定义?"从给定线程中获取同步语言"问题的所有答案似乎都不一致否定了这种可能性......
最后,但并非最不重要:
短语"当前SynchronizationContext是当前线程的属性"正确"正确吗?
那么,我怎样才能获得不同特定线程实例的此属性的值?
(*)
最近,我基本上使用winforms获得了C#WPF应用程序代码.
c# multithreading synchronizationcontext task-parallel-library thread-synchronization