这可能很简单,但目前还无法弄清楚。
简单来说:
我的存储库层中有一个长时间运行的操作(大约 8 分钟)。
public static ReleaseSelection LoadedReleaseSelection = new ReleaseSelection();
private static object s_cacheLock = new object();
public Long Load(ReleaseSelection releaseSelection)
{
//check if the release passed in to load is different the one previously loaded
if (releaseSelection != LoadedReleaseSelection)
{
//do something to break the lock(s_cacheLock)
}
lock (s_cacheLock)
{
//Reads from TAB files and put them into cache objects.. runs for about 8 mins
LoadedReleaseSelection = releaseSelection;
}
}
服务层异步调用Load
public Task<long> LoadAsync()
{
ReleaseSelection releaseSelection = //get value from db through another repo call
if (releaseSelection == null)
{
return null;
}
return Task.Factory.StartNew(() => m_releaseRepository.Load(releaseSelection));
}
最后,该服务由 API 端点调用
public async Task<IHttpActionResult> ReleaseLoadPost()
{
await m_releaseService.LoadAsync();
return Ok();
}
当以下条件成立时,我如何取消加载操作(第一个代码块)中的
lock(s_cacheLock)
//check if the release passed in to load is different the one previously loaded
if (releaseSelection != LoadedReleaseSelection)
{
//do something to break the lock(s_cacheLock)
}
这样另一个线程就不必等待之前的加载完成?
注意:我需要
lock(m_cacheLock)
因为我还有其他从缓存中读取的方法,并且在加载所有缓存之前不应该这样做。
不需要使用锁来保护8分钟的加载过程,只需在加载完成后锁定更新缓存集语句即可。您还应该使用
CancellationToken
取消加载过程,并在加载过程中定期检查令牌取消状态。
另一种选择是进行一些重构并使用 SemaphoreSlim,它有一个 Wait(CancellationToken) 方法。
如果你看一下底层代码,它会做一堆尝试高效地完成任务(通过 SpinWait,然后使用 Monitor 和 WaitHandle),但如果所有其他方法都失败,它确实会启动自己的异步任务,该任务将在以下情况下被取消:您的取消令牌已被取消。这是一种您通常不希望自己实现和测试/调试的代码,并且在 .NET 版本上明显以各种方式演变。
使用Monitor.Enter & Monitor.Exit代替锁,确保捕获异常并释放锁。
示例:
Monitor.Enter(s_cacheLock)
// do work
Monitor.Exit(s_cacheLock)