强制同步上下文发布到控制台应用中的主线程



我有一个使用 EF Core 的 .NET Core 控制台应用程序。大多数繁重的工作都是通过一些异步方法完成的,然后我从延续中更新我的 EF 上下文。不幸的是,当您尝试从多个线程使用 DbContext 时,EF 会变得有点烦躁,因此我一直在尝试让我的延续在创建上下文的同一线程中运行(在本例中为线程 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);
}
}
}

我的直觉告诉我,问题出在自定义同步上下文的Post方法中,因为它是从运行任务的同一线程调用的,我没有告诉它它需要在主线程上运行。我有一个错误的假设,即由于上下文是在线程 1 中实例化的,那么它发布的任何内容也会在那里运行。我认为这就是我陷入困境的地方,我不确定最好的方法,所以建议表示赞赏。

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

谢谢!!

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

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

最新更新