.net 如何支持多线程异步锁定。。。可能是分布式锁定

.net 如何支持多线程异步锁定。。。可能是分布式锁定,.net,multithreading,asynchronous,locking,.net,Multithreading,Asynchronous,Locking,我的问题涉及到在多线程异步系统中保护关键资源。它是一个用Asp.NETMVC、WebApi以及托管在IIS中的WCF项目编写的web应用程序。关键区域是缓存(目前是内存中的System.Runtime.MemoryCache…但请参见下文,也可以是分布式AppFabric/Reddis缓存) 背景域 我在医疗保健部门工作,我们收集患者信息,例如他们的文档列表(如转录本、报告)、遭遇事件(如入院)和实验室(如血液检测)。我们有一个PatientChart对象跟踪所有这些数据(例如,所有文档的ID+

我的问题涉及到在多线程异步系统中保护关键资源。它是一个用Asp.NETMVC、WebApi以及托管在IIS中的WCF项目编写的web应用程序。关键区域是缓存(目前是内存中的System.Runtime.MemoryCache…但请参见下文,也可以是分布式AppFabric/Reddis缓存)

背景域 我在医疗保健部门工作,我们收集患者信息,例如他们的文档列表(如转录本、报告)、遭遇事件(如入院)和实验室(如血液检测)。我们有一个PatientChart对象跟踪所有这些数据(例如,所有文档的ID+标题,但不是实际内容)

我们系统中的工作流示例

  • 用户选择患者
  • 请求预取所有基础数据
  • 启动后台线程/任务以异步完成工作
  • 单个数据与“PatientChart”一样存储在缓存中 反对
示例代码 如何保护关键区域,即缓存中的PatientChart?下面是我现在使用信号量的精简版本:

public class PatientProcessorService 
{
    private static readonly SemaphoreSlim _semaphoreSlim = new SemaphoreSlim(initialCount: 1); 

    // this method called asynchronously via multiple parallel threads, based on a master list of document Ids
    private async Task<Document> GetDocument(string patientId, int documentId)
    {
        Document document = _documentService.Get(patientId, documentId);
        await _cacheService.Set(documentCacheKey, document);

        PatientChart patientChart = null;
        await _semaphoreSlim.WaitAsync();
        try
        {
            patientChart = await _cacheService.Get(cacheKey) as PatientChart;
            if (patientChart != null)
            {
                // update the patient chart with whatever new data/information we have
                patientChart.Message = "Document + " documentId + " retrieved";
                patientChart.LastUpdated = DateTime.Now;
                patientChart.Documents.Add(new DocumentMetaInfo() {
                    Id=documentId, 
                    Title=document.Title, 
                    CacheKeyLocation=documentCacheKey});
                await _cacheService.Set(cacheKey, patientChart);                  
            }

        }
        finally {
            _semaphoreSlim.Release();
        }
    }
 }
公共类PatientProcessorService
{
private static readonly SemaphoreSlim _SemaphoreSlim=新的SemaphoreSlim(initialCount:1);
//此方法基于文档ID的主列表,通过多个并行线程异步调用
私有异步任务GetDocument(字符串patientId,int documentId)
{
documentdocument=\u documentService.Get(patientId,documentId);
wait_cacheService.Set(documentCacheKey,document);
PatientChart PatientChart=null;
wait_semaphoreSlim.WaitAsync();
尝试
{
patientChart=wait_cacheService.Get(cacheKey)作为patientChart;
if(patientChart!=null)
{
//使用我们掌握的任何新数据/信息更新病历
patientChart.Message=“文档+”文档ID+“已检索”;
patientChart.LastUpdated=DateTime.Now;
patientChart.Documents.Add(新的DocumentMetaInfo(){
Id=documentId,
Title=document.Title,
CacheKeyLocation=documentCacheKey});
wait_cacheService.Set(cacheKey,patientChart);
}
}
最后{
_semaphoreSlim.Release();
}
}
}
一些问题

1) 上述说法有意义吗?有什么明显的问题吗

2) 今天,上面的代码只是一个原型,用来证明各种事情。但是,如果我们计划推出这种体系结构,我们很可能不得不转向分布式缓存(如AppFabric、Redis),并引入持久队列(如MSMQ、RabbitMQ)和windows服务(与IIS中的后台任务/线程相反),以提供负载平衡和可扩展性

在这种情况下,我假设我的锁必须实现得更低(因为我可能有不同的进程访问缓存)。e、 g.如果我使用AppFabric,我是否必须使用GetAndLock和PutAndUnlock api,加上Guy Godin编写的支持“块+等待”逻辑(通过异常、重试和超时逻辑)的扩展

我相信它模仿了Redis的实现:

更新后的代码可能是这样的

public class PatientProcessorService 
{
    // this method called asynchronously via multiple parallel threads, based on a master list of document Ids
    private async Task<Document> GetDocument(string patientId, int documentId)
    {
        Document document = _documentService.Get(patientId, documentId);
        myAppFabricCache.Set(documentCacheKey, document);

        PatientChart patientChart = null;
        // e.g. using appfabric DataCache and extension from Guy Godin in above link
        using (myAppFabricCache.AquireLock(cacheKey, timeOut)
        {
            patientChart = myAppFabricCache.Get(cacheKey) as PatientChart;
            if (patientChart != null)
            {
                // update the patient chart with whatever new data/information we have
                patientChart.Message = "Document + " documentId + " retrieved";
                patientChart.LastUpdated = DateTime.Now;
                patientChart.Documents.Add(new DocumentMetaInfo() {
                    Id=documentId, 
                    Title=document.Title, 
                    CacheKeyLocation=documentCacheKey});
                }
                myAppFabricCache.Set(cacheKey, patientChart); 
            }
        }
    }
公共类PatientProcessorService
{
//此方法基于文档ID的主列表,通过多个并行线程异步调用
私有异步任务GetDocument(字符串patientId,int documentId)
{
documentdocument=\u documentService.Get(patientId,documentId);
myAppFabricCache.Set(documentCacheKey,document);
PatientChart PatientChart=null;
//例如,在上述链接中使用来自Guy Godin的appfabric数据缓存和扩展
使用(myAppFabricCache.AquireLock)(缓存键,超时)
{
patientChart=myAppFabricCache.Get(cacheKey)作为patientChart;
if(patientChart!=null)
{
//使用我们掌握的任何新数据/信息更新病历
patientChart.Message=“文档+”文档ID+“已检索”;
patientChart.LastUpdated=DateTime.Now;
patientChart.Documents.Add(新的DocumentMetaInfo(){
Id=documentId,
Title=document.Title,
CacheKeyLocation=documentCacheKey});
}
设置(cacheKey,patientChart);
}
}
}
3) 上面的问题是,我甚至不知道如何重构代码,使其与cahce实现无关。。。因此,关于如何做到这一切的建议将不胜感激


谢谢你读到这里

patientChart的一生是什么。从高层次的角度来看,patientChart是患者的“记录簿”吗?是的,它就像一本记录簿。设想一下,它也将用于登录页/仪表板类型的视图中。生命周期将取决于微调-保存在缓存中,例如15分钟,或者可能一天…您谈论的是持久队列和分布式缓存。我忍不住想,这可能是利用NServiceBus作为队列上的流程管理器,并利用SOA来拆分域/上下文的候选方案。我甚至会考虑删除CQRS——尤其是如果你读的比你写的多,而且你打算扩大你的阅读方面。