using System; using Microsoft.Extensions.Caching.Memory; using System.Threading; namespace IStation { internal sealed partial class SharedMemoryCache { /// /// Attempts to get cache value of type T, otherwise sets a cache item by key, function and optional eviction /// /// /// A unique identifier for the cache entry to insert /// A function to execute to get the value for the cache entry /// (Optional) An object that contains eviction details for the cache entry /// An object of type T, or default T public T GetSet(string key, Func valueFunction, int? seconds) { bool found; T value = this.Get(key, out found); Exception ex_func = null; if (!found) { this._cacheKeysBeingHandled.TryAdd(key, new CacheKeyBeingHandled()); lock (this._cacheKeysBeingHandled[key].GetSetOperation.Lock) { // re-check to see if another thread beat us to setting this value value = this.Get(key, out found); if (!found) { // set, so function can be cancelled if a direct set of the same type occurs this._cacheKeysBeingHandled[key].GetSetOperation.Type = typeof(T); // put the function into it's own thread (so it can be cancelled) this._cacheKeysBeingHandled[key].GetSetOperation.Thread = new Thread(() => { var aborted = false; var success = false; try { value = valueFunction(); success = true; } catch (ThreadAbortException) { aborted = true; } #region 用于处理valueFunction引发异常 catch (Exception ex) { aborted = true; ex_func = ex; } #endregion finally { if (aborted) { value = (T)this._cacheKeysBeingHandled[key].GetSetOperation.Value; // cast should always be valid as this method is the only thing that sets it } else if (success) { if (value == null) { if (!this._isWiping) { ((IMemoryCacheDirect)this).Remove(key); } } else { ((IMemoryCacheDirect)this).Set(key, value, seconds); } } } }); this._cacheKeysBeingHandled[key].GetSetOperation.Thread.Start(); this._cacheKeysBeingHandled[key].GetSetOperation.Thread.Join(); } } this._cacheKeysBeingHandled.TryRemove(key, out CacheKeyBeingHandled cacheKeyBeingHandled); } if (ex_func != null) throw ex_func; return value; } /// /// Attempts to get cache value of type T, otherwise sets a cache item by key, value and optional eviction /// /// /// A unique identifier for the cache entry to insert /// The data for the cache entry /// (Optional) An object that contains eviction details for the cache item /// An object of type T, or default T public T GetSet(string key, T value, int? seconds) { bool found; var cachedValue = this.Get(key, out found); if (!found) { Set(key, value, seconds); return value; } return cachedValue; } } }