c# - Async threadsafe Get from MemoryCache -
i have created async cache uses .net memorycache
underneath. code
public async task<t> getasync(string key, func<task<t>> populator, timespan expire, object parameters) { if(parameters != null) key += jsonconvert.serializeobject(parameters); if(!_cache.contains(key)) { var data = await populator(); lock(_cache) { if(!_cache.contains(key)) //check again locked time _cache.add(key, data, datetimeoffset.now.add(expire)); } } return (t)_cache.get(key); }
i think downside need todo await outside lock populator isnt thread safe, since await cant reside inside lock guess best way. there pitfalls have missed?
update: version of esers answer threadsafe when antoher thread invalidates cache
public async task<t> getasync(string key, func<task<t>> populator, timespan expire, object parameters) { if(parameters != null) key += jsonconvert.serializeobject(parameters); var lazy = new lazy<task<t>>(populator, true); _cache.addorgetexisting(key, lazy, datetimeoffset.now.add(expire)); return ((lazy<task<t>>) _cache.get(key)).value; }
it can slower because creates lazy instances never executed , uses lazy in full threadsafe mode lazythreadsafetymode.executionandpublication
update new benchmark (higher better)
lazy lock 42535929 lazy getoradd 41070320 (only solution thread safe) semaphore 64573360
a simple solution use semaphoreslim.waitasync()
instead of lock, , around issue of awaiting inside lock. although, other methods of memorycache
thread-safe.
private semaphoreslim semaphoreslim = new semaphoreslim(1); public async task<t> getasync( string key, func<task<t>> populator, timespan expire, object parameters) { if (parameters != null) key += jsonconvert.serializeobject(parameters); if (!_cache.contains(key)) { await semaphoreslim.waitasync(); try { if (!_cache.contains(key)) { var data = await populator(); _cache.add(key, data, datetimeoffset.now.add(expire)); } } { semaphoreslim.release(); } } return (t)_cache.get(key); }
Comments
Post a Comment