//The item is expired, call the "Refresh" action of this item to get a refresh value //1. If there's not enough worker threads, the dispatching thread will wait until the below 2 conditions happend: //(1). There is worker threads available //(2). There is user event comming (Add/Remove etc.) //2. If there's at least one worker thread, the dispatching thread will schedule the "Refresh" action in one worker thread //after the worker thread end to refresh the cache item, it will release the worker thread resource, and notify the //dispatching thread, so that other expired cache items wait for refresh worker thread can do their refresh job private bool RefreshExpiredCache(SchedulerKey sk, CacheItem ci) { //Refresh the expired item now, but firstly we should get a token if (Interlocked.Decrement(ref m_availRefreshThreadCount) < 0) { //Got an invalid token, return it Interlocked.Increment(ref m_availRefreshThreadCount); //No available refresh thread now, wait for one LogManager.Info("CacheManager:ProcessTimerEvents", string.Format("Begin to wait for refreshing thread token with key [{0}]", sk.Key)); m_waitHandle.WaitOne(); //Either there's an user event comming or a refresh thread token is available LogManager.Info("CacheManager:ProcessTimerEvents", string.Format("End to wait for refreshing thread token with key [{0}]", sk.Key)); //No need to wait again, start a new loop return false; } //Save the last expiration time ci.LastExpirationInfo.LastExpiration = ci.NextExpirationTime; //Update the next expiration time for next schedule ci.NextExpirationTime = ci.Expiration.NextExpiration(ci.LastExpirationInfo); //Validation TimeSpan ts = ci.NextExpirationTime - ci.LastExpirationInfo.LastExpiration; if (ts.TotalMilliseconds < MIN_WAIT_TIME) { LogManager.Warn("CacheManager:ProcessTimerEvents", "Too short time interval, ts=" + ts.TotalMilliseconds); //wait 3s ci.NextExpirationTime = ci.LastExpirationInfo.LastExpiration.AddMilliseconds(3000); } //Re-schedule again m_heap.Remove(sk); m_heap.Add(new SchedulerKey(ci.NextExpirationTime, sk.Key), DUMMY); //Fire the refresh action Task.Factory.StartNew(() => { try { ci.RefreshAsync(); //Release the token Interlocked.Increment(ref m_availRefreshThreadCount); //And notify the listeners m_waitHandle.Set(); } catch (Exception ex) { LogManager.Error(string.Format("Task for key [{0}] exception", ci.Key), ex); } }); return true; }