Multithreading 每个请求的Asp.Net核心异步延迟初始化
对于我们的多租户Asp.Net Core 2.2 web应用程序中的许多端点,我们需要根据每个请求(使用异步数据库调用)从数据库中获取一些租户信息,然后根据需要重用请求管道中各个点的信息,而无需每次进行数据库调用。也就是说,我们希望在每个请求的基础上使用类似于惰性异步属性的东西 根据我读过的各种文档,范围服务的实例一次只能由一个线程(处理请求的线程)访问。因此,我相信这意味着我应该能够安全地执行以下操作:Multithreading 每个请求的Asp.Net核心异步延迟初始化,multithreading,asp.net-core,dependency-injection,thread-safety,lazy-initialization,Multithreading,Asp.net Core,Dependency Injection,Thread Safety,Lazy Initialization,对于我们的多租户Asp.Net Core 2.2 web应用程序中的许多端点,我们需要根据每个请求(使用异步数据库调用)从数据库中获取一些租户信息,然后根据需要重用请求管道中各个点的信息,而无需每次进行数据库调用。也就是说,我们希望在每个请求的基础上使用类似于惰性异步属性的东西 根据我读过的各种文档,范围服务的实例一次只能由一个线程(处理请求的线程)访问。因此,我相信这意味着我应该能够安全地执行以下操作: // Registered as scoped in the DI container p
// Registered as scoped in the DI container
public class TenantInfoRetriever : ITenantInfoRetriever
{
private TenantInfo _tenantInfo;
private bool _initialized;
public async Task<TenantInfo> GetAsync()
{
// This code is obviously not thread safe, which is why it's important that no two threads are running here at the same time.
if (!_initialized)
{
_tenantInfo = await GetTenantInfoAsync(); // this might actually legitimately return null, hence the need for the separate property "_initialized"
_initialized = true;
}
return _tenantInfo;
}
private async Task<TenantInfo> GetTenantInfoAsync()
{
return await DoDatabaseCallToGetTenantInfo(); // of course this would use an injected EfContext instance (scoped service)
}
}
//在DI容器中注册为作用域
公共类租户ForeTriever:ITenantInformationRetriever
{
私人租户信息(u TenantInfo);;
已初始化私有布尔;
公共异步任务GetAsync()
{
//这段代码显然不是线程安全的,这就是为什么不能有两个线程同时运行的原因。
如果(!\u已初始化)
{
_tenantInfo=await GetTenantInfoAsync();//这实际上可能会合法地返回null,因此需要单独的属性“\u initialized”
_初始化=真;
}
返回租户信息;
}
私有异步任务GetTenantInfoAsync()
{
return await DoDatabaseCallToGetTenantInfo();//当然这将使用注入的EfContext实例(作用域服务)
}
}
据我所知,这应该是可行的,因为作用域服务不需要是线程安全的
我的问题:我的假设是否正确,或者我是否遗漏了一些重要的东西
根据我读过的各种文档,作用域服务的实例一次只能由一个线程(处理请求的线程)访问
这不完全正确。当您等待操作时,线程将返回到线程池,当异步操作恢复时,将从线程池中提取一个空闲线程。
它不需要与启动它的线程完全相同
但是是的,当它的作用域被限定时,不会有两个线程同时访问它,也就是说,从ASP.NET核心端
当然,您也需要对自己的代码采取预防措施。
如果您启动两个任务/线程并同时运行它们,那么仍有可能同时访问它们
如果担心被访问,请执行双重检查锁定模式(使用volatile字段)或锁定互斥体(不是您修改的值的对象)或使用AsyncLazy
,即从
示例(摘自)
例1:
string name;
public string Name
{
get
{
lock (mutex) // GOOD: Thread-safe
{
if (name == null)
name = LoadNameFromDatabase();
return name;
}
}
}
例2:
volatile string name; // GOOD: Thread-safe
public string Name
{
get
{
if (name == null)
{
lock (mutex)
{
if (name == null)
name = LoadNameFromDatabase();
}
}
return name;
}
}
根据我读过的各种文档,作用域服务的实例一次只能由一个线程(处理请求的线程)访问。不完全正确,因为当您等待操作时,线程将返回到线程池,当它恢复时,将从线程池中提取一个空闲线程。它不需要与启动它的线程完全相同。但是是的,不会有两个线程同时访问它。但是你有什么问题?如果您担心它会被访问,请使用双锁模式或者使用
AsyncLazy
(请参阅)。(删除了上一个链接,因为它引用的是visualstudio ts库,不应在Visual Studio扩展包Hanks@Tseng之外使用。我想我更喜欢保持简单,即不必担心锁定。根据您所说的,没有两个线程会同时访问此服务的实例,因此,这对我来说意味着锁定是不必要的。类似地,AsyncLazy方法似乎是为多线程场景设计的,所以我认为这有点过分。我的问题主要是为了确保在继续我的简单方法之前,我没有忽略一些关键的东西。如果您有兴趣将您的评论映射到回答:我很乐意将其标记为已接受。