Asp.net Core DI:使用SemaphoreSlim进行Singleton的写入和读取操作

问题描述 投票:0回答:2

我正在重新设计ASP.NET CORE 2.2应用程序,以避免将服务定位器模式与静态类结合使用。双坏!

重新工具涉及创建和注入Singleton对象作为某些全局数据的存储库。这里的想法是为了避免在我的SQL服务器上点击一些在请求中反复使用的基本/全局数据。但是,这些数据需要每小时更新一次(不仅仅是在应用启动时)。因此,为了管理这种情况,我使用SemaphoreSlim来处理对数据对象的一次访问。

这是我正在做的事情的配对草图:

namespace MyApp.Global
{

    public interface IMyGlobalDataService
    {
        Task<List<ImportantDataItem>> GetFilteredDataOfMyList(string prop1);
        Task LoadMyImportantDataListAsync();
    }

    public class MyGlobalDataService: IMyGlobalDataService
    {
        private MyDbContext _myDbContext;

        private readonly SemaphoreSlim myImportantDataLock = new SemaphoreSlim(1, 1);
        private List<ImportantDataItem> myImportantDataList { get; set; }
        public async Task<List<ImportantDataItem>> GetFilteredDataOfMyList(string prop1)
        {
            List<ImportantDataItem> list;
            myImportantDataLock.WaitAsync();
            try
            {
                list = myImportantDataList.Where(itm => itm.Prop1 == prop1).ToList();
            }
            finally
            {
                myImportantDataLock.Release();
            }
            return list;
        }

        public async Task LoadMyImportantDataListAsync()
        {
            // this method gets called when the Service is created and once every hour thereafter

            myImportantDataLock.WaitAsync();
            try
            {
                this.MyImportantDataList = await _myDbContext.ImportantDataItems.ToListAsync();
            }
            finally
            {
                myImportantDataLock.Release();
            }
            return;
        }


        public MyGlobalDataService(MyDbContext myDbContext) {
            _myDbContext = myDbContext;
        };
    }
}

所以实际上我使用SemaphoreSlim来限制一次一个线程的访问,对于myImportantDataList进行READ和UPDATING。这对我来说真是不确定的领域。这似乎是一种处理我在整个应用程序中注入全局数据Singleton的合适方法吗?或者我应该期待疯狂的线程锁定/阻塞?

c# dependency-injection singleton semaphore asp.net-core-2.2
2个回答
1
投票

使用SemaphoreSlim的问题是可伸缩性。

由于这是在Web应用程序中,因此可以假设您希望有多个读取器同时访问数据。但是,您(可以理解)将可以同时生成的信号量请求数限制为1(以防止并发读写请求)。这意味着您也将序列化所有读取。

你需要使用类似ReaderWriterLockSlim的东西来允许多个线程进行读取,但要确保写入的独占访问权限。


1
投票

Creyke的答案对我来说很重要:使用ReaderWriterLockSlim。所以我把它标记为已接受的答案。但我发布了我的修订后的解决方案,以防任何人都有帮助。需要注意的是,我正在使用以下软件包为ReaderWriterLockSlim提供异步功能:https://www.nuget.org/packages/Nito.AsyncEx/

using Nito.AsyncEx;
using System;
using System.Collections.Generic;
using System.Text;

namespace MyApp.Global
{

    public interface IMyGlobalDataService
    {
        Task<List<ImportantDataItem>> GetFilteredDataOfMyList(string prop1);
        Task LoadMyImportantDataListAsync();
    }

    public class MyGlobalDataService : IMyGlobalDataService
    {
        private MyDbContext _myDbContext;

        private readonly AsyncReaderWriterLock myImportantDataLock = new AsyncReaderWriterLock();
        private List<ImportantDataItem> myImportantDataList { get; set; }
        public async Task<List<ImportantDataItem>> GetFilteredDataOfMyList(string prop1)
        {
            List<ImportantDataItem> list;
            using (await myImportantDataLock.ReaderLockAsync())
            {
                list = myImportantDataList.Where(itm => itm.Prop1 == prop1).ToList();
            }
            return list;
        }

        public async Task LoadMyImportantDataListAsync()
        {
            // this method gets called when the Service is created and once every hour thereafter

            using (await myImportantDataLock.WriterLockAsync())
            {
                this.MyImportantDataList = await _myDbContext.ImportantDataItems.ToListAsync();
            }
            
            return;
        }


        public MyGlobalDataService(MyDbContext myDbContext)
        {
            _myDbContext = myDbContext;
        };
    }
}
© www.soinside.com 2019 - 2024. All rights reserved.