强制SynchronizationContext发布到控制台应用程序中的主线程

时间:2018-08-22 13:09:57

标签: c# multithreading asynchronous async-await synchronizationcontext

我有一个使用EF Core的.NET Core控制台应用程序。大多数繁重的工作都是通过一些异步方法完成的,然后我从一个连续的实例中更新了我的EF上下文。不幸的是,当您尝试从多个线程中使用DbContext时,EF有点生气,所以我一直试图让我的延续在创建上下文的同一线程中运行(在这种情况下,线程1 /主线程)。

这是一个简单的示例类,用于说明我尝试采用的方法,我想我已经接近了,但我缺少一些关键的内容,因为延续继续与{{ 1}}方法。

DoGreeting

我的直觉告诉我问题出在我的自定义同步上下文的public class Worker { private static readonly MyContext _ctx = new MyContext(); public void Run() { SynchronizationContext.SetSynchronizationContext(_ctx); Console.WriteLine($"Synchronization context set in thread {Thread.CurrentThread.ManagedThreadId}"); DoGreeting("Bob").Wait(); Console.ReadKey(); } public async Task DoGreeting(string Name) { Console.WriteLine($"DoGreeting was called from thread {Thread.CurrentThread.ManagedThreadId}"); await Task.Run(() => { SayHi(Name); }); SayGoodbye(Name); } public void SayHi(string Name) { Console.WriteLine($"Hello {Name} from thread {Thread.CurrentThread.ManagedThreadId}"); } public void SayGoodbye(string Name) { Console.WriteLine($"Goodbye {Name} from thread {Thread.CurrentThread.ManagedThreadId}"); } private class MyContext : SynchronizationContext { public override void Post(SendOrPostCallback d, object state) { Console.WriteLine($"Post called from thread {Thread.CurrentThread.ManagedThreadId}"); d(state); } } } 方法中,因为它是从任务运行所在的线程中调用的,而我没有告诉它需要运行回到主线程上。我有一个错误的假设:既然上下文是在线程1中实例化的,那么它发布的任何内容都将在该线程中运行。我认为这是我遇到的问题,我不确定最好的方法,因此建议不胜感激。

我知道在异步应用程序中运行EF Core的方法有很多,但是大多数解决方案都是为ASP.NET Core或其他使用DI的应用程序量身定制的。但是,我的应用程序是没有DI的控制台应用程序。控制台应用程序中的EF Core建议当然是受欢迎的(并且我可以解决我的问题),但是我也想知道如何在我再次遇到这种情况的情况下,强制在主线程上运行延续。未来。

谢谢!

1 个答案:

答案 0 :(得分:0)

首先,在控制台应用程序或Web应用程序中的DI设计之间没有概念上的区别,因为您需要进行的所有设置是注册阶段和解决阶段,在ASP.NET Core或MVC的情况下,已完成此阶段取决于基础架构,而对于控制台应用程序,则应手动进行。我不知道您的应用程序的结构以及应提供多少个流程,但是EF的最佳实践是为每个流程创建单独的上下文(如果是Web应用程序,则流程是请求处理,如果是WPF或Win,表单可以是表单,等等(有关更多详细信息,请参见here)。因此,请考虑在您的应用程序中遵循这些建议。

关于第二个问题,如果它再次与控制台应用程序相关,则需要在Program.Main方法或由其调用的方法中组织某种消息泵。为了安全起见,消息泵应该从某个共享队列(例如,基于BlockingCollection)读取消息,并执行其处理程序。然后,在自定义同步上下文中的Post方法仅应将消息放入队列中,仅此而已(符合“即发即弃”的语义),而真正的执行应在主消息泵内进行。如果需要异常处理,则必须考虑实现同步Send方法,以在执行完成后从另一个完成队列中获取异常。