| | |
| | | public async Task<WebResponseContent> BatchOrderFeedbackToMes(List<string> orderNos, int inout) |
| | | { |
| | | try |
| | | { // 1. ãå
å鿢å ã |
| | | //if (MemoryLockManager.TryAcquireLock(orderNos[0])) |
| | | //{ |
| | | { |
| | | |
| | | if (inout == 1) |
| | | { |
| | |
| | | |
| | | public static class MemoryLockManager |
| | | { |
| | | // åå¨èµæºIDåå
¶å¯¹åºçé对象ãä½¿ç¨ ConcurrentDictionary ç¡®ä¿å¯¹åå
¸æä½æ¬èº«ç线ç¨å®å
¨ã |
| | | private static readonly ConcurrentDictionary<string, object> _resourceLocks = new ConcurrentDictionary<string, object>(); |
| | | // åå¨èµæºéçå
æ°æ®ï¼éå¯¹è±¡ãææçº¿ç¨ãå ç¨æ¶é´ãè¶
æ¶æ¶é´ï¼ |
| | | private class LockMetadata |
| | | { |
| | | public object LockObject { get; } = new object(); |
| | | public int HoldingThreadId { get; set; } = -1; // ææéç线ç¨ID |
| | | public DateTime AcquireTime { get; set; } // è·åéçæ¶é´ |
| | | public TimeSpan Timeout { get; set; } // éè¶
æ¶æ¶é´ |
| | | public bool IsReleased { get; set; } // æ¯å¦å·²éæ¾ |
| | | } |
| | | |
| | | // å
¨å±éæéï¼ç¨äºä¿æ¤ _resourceLocks åå
¸ä¸ GetOrAdd æ TryRemove æ¶çç«äº |
| | | private static readonly object _globalLocker = new object(); |
| | | // èµæºID -> éå
æ°æ® |
| | | private static readonly ConcurrentDictionary<string, LockMetadata> _resourceLocks = new(); |
| | | // å
¨å±éï¼ä¿æ¤éå
æ°æ®çå建/å é¤ï¼ |
| | | private static readonly object _globalLocker = new(); |
| | | // éæºæ°çæå¨ï¼ç¨äºçæ3-5ç§éæºè¶
æ¶ï¼ |
| | | private static readonly Random _random = new Random(); |
| | | |
| | | /// <summary> |
| | | /// å°è¯éå®ä¸ä¸ªèµæºIDã |
| | | /// å°è¯éå®èµæºï¼å¸¦è¶
æ¶èªå¨éæ¾ï¼ |
| | | /// </summary> |
| | | /// <param name="resourceId">è¦éå®çèµæºIDï¼ä¾å¦ InboundRecord IDï¼</param> |
| | | /// <param name="resourceId">èµæºID</param> |
| | | /// <param name="timeoutSeconds">è¶
æ¶æ¶é´ï¼é»è®¤3-5ç§éæºï¼</param> |
| | | /// <returns>æ¯å¦æåè·åé</returns> |
| | | public static bool TryAcquireLock(string resourceId) |
| | | public static bool TryAcquireLock(string resourceId, int? timeoutSeconds = null) |
| | | { |
| | | object lockObject = null; |
| | | if (string.IsNullOrEmpty(resourceId)) |
| | | throw new ArgumentNullException(nameof(resourceId)); |
| | | |
| | | // æ ¸å¿æè·¯ï¼ä¸ºæ¯ä¸ªèµæºå建ä¸ä¸ªå¯ä¸çé对象 |
| | | // ç¡®å®è¶
æ¶æ¶é´ï¼3-5ç§éæºï¼ |
| | | var timeout = TimeSpan.FromSeconds(timeoutSeconds ?? _random.Next(3, 6)); |
| | | var currentThreadId = Thread.CurrentThread.ManagedThreadId; |
| | | |
| | | LockMetadata lockMeta = null; |
| | | lock (_globalLocker) |
| | | { |
| | | // å¦æèµæºIDä¸å¨åå
¸ä¸ï¼åæ·»å ä¸ä¸ªæ°çé对象 |
| | | // å¦åï¼ä½¿ç¨å·²åå¨çé对象 |
| | | lockObject = _resourceLocks.GetOrAdd(resourceId, new object()); |
| | | // è·åæå建éå
æ°æ® |
| | | lockMeta = _resourceLocks.GetOrAdd(resourceId, key => new LockMetadata()); |
| | | |
| | | // 鲿¢éå¤è·åï¼å½å线ç¨å·²ææéï¼ |
| | | if (lockMeta.HoldingThreadId == currentThreadId && !lockMeta.IsReleased) |
| | | return true; // 线ç¨å¯éå
¥ |
| | | } |
| | | |
| | | // å°è¯è·åèµæºç¹å®çé |
| | | // ä½¿ç¨ Monitor.TryEnter é¿å
é»å¡ï¼å¹¶å®ç°éé»å¡çæ¢é |
| | | return Monitor.TryEnter(lockObject); |
| | | // å°è¯è·åéï¼éé»å¡ï¼ |
| | | if (!Monitor.TryEnter(lockMeta.LockObject)) |
| | | return false; |
| | | |
| | | // æ è®°éææç¶æ |
| | | lockMeta.HoldingThreadId = currentThreadId; |
| | | lockMeta.AcquireTime = DateTime.Now; |
| | | lockMeta.Timeout = timeout; |
| | | lockMeta.IsReleased = false; |
| | | |
| | | // å¯å¨è¶
æ¶èªå¨éæ¾ä»»å¡ |
| | | _ = Task.Delay(timeout).ContinueWith(_ => |
| | | { |
| | | try |
| | | { |
| | | ReleaseLock(resourceId, force: true); |
| | | } |
| | | catch (Exception ex) |
| | | { |
| | | // è®°å½è¶
æ¶éæ¾å¼å¸¸ |
| | | Console.WriteLine($"èµæº[{resourceId}]è¶
æ¶èªå¨éæ¾å¤±è´¥ï¼{ex.Message}"); |
| | | } |
| | | }); |
| | | |
| | | return true; |
| | | } |
| | | |
| | | /// <summary> |
| | | /// éæ¾èµæºIDçéå®ã |
| | | /// éæ¾èµæºé |
| | | /// </summary> |
| | | /// <param name="resourceId">è¦éæ¾çèµæºID</param> |
| | | public static void ReleaseLock(string resourceId) |
| | | /// <param name="resourceId">èµæºID</param> |
| | | /// <param name="force">æ¯å¦å¼ºå¶éæ¾ï¼è¶
æ¶èªå¨éæ¾æ¶ä½¿ç¨ï¼</param> |
| | | public static void ReleaseLock(string resourceId, bool force = false) |
| | | { |
| | | if (_resourceLocks.TryGetValue(resourceId, out object lockObject)) |
| | | { |
| | | // ç¡®ä¿éæ¾çæ¯å½åçº¿ç¨ææçé |
| | | if (Monitor.IsEntered(lockObject)) |
| | | { |
| | | Monitor.Exit(lockObject); |
| | | if (string.IsNullOrEmpty(resourceId)) |
| | | throw new ArgumentNullException(nameof(resourceId)); |
| | | |
| | | // éæ¾éåï¼å°è¯ä»åå
¸ä¸ç§»é¤è¿ä¸ªéå¯¹è±¡ï¼æ¸
çå
åã |
| | | // å¿
é¡»å¨ Monitor.Exit ä¹åæ§è¡ã |
| | | if (!_resourceLocks.TryGetValue(resourceId, out var lockMeta)) |
| | | return; |
| | | |
| | | var currentThreadId = Thread.CurrentThread.ManagedThreadId; |
| | | |
| | | // æ ¡éªéæ¾åæ³æ§ï¼ä»
ææéççº¿ç¨æå¼ºå¶éæ¾å¯æ§è¡ |
| | | if (!force && lockMeta.HoldingThreadId != currentThreadId) |
| | | { |
| | | // éææçº¿ç¨å°è¯éæ¾ï¼æåºå¼å¸¸æè¿åï¼æ ¹æ®ä¸å¡éæ©ï¼ |
| | | throw new InvalidOperationException($"线ç¨[{currentThreadId}]æ æéæ¾èµæº[{resourceId}]çéï¼å½åææçº¿ç¨ï¼{lockMeta.HoldingThreadId}ï¼"); |
| | | } |
| | | |
| | | // åéæ ¡éªéç¶æ |
| | | lock (_globalLocker) |
| | | { |
| | | _resourceLocks.TryRemove(resourceId, out _); |
| | | if (lockMeta.IsReleased) |
| | | return; |
| | | |
| | | // ç¡®ä¿é被å½åçº¿ç¨ææï¼å¼ºå¶éæ¾é¤å¤ï¼ |
| | | if (Monitor.IsEntered(lockMeta.LockObject)) |
| | | { |
| | | try |
| | | { |
| | | Monitor.Exit(lockMeta.LockObject); |
| | | } |
| | | catch (SynchronizationLockException) |
| | | { |
| | | // å·²è¢«éæ¾ï¼å¿½ç¥ |
| | | return; |
| | | } |
| | | } |
| | | |
| | | // æ è®°é已鿾 |
| | | lockMeta.IsReleased = true; |
| | | lockMeta.HoldingThreadId = -1; |
| | | |
| | | // å»¶è¿æ¸
çéå
æ°æ®ï¼é¿å
å¹¶ååå»ºï¼ |
| | | // çå¾
1ç§åæ¸
çï¼é²æ¢åéæ¾å°±ææ°çº¿ç¨æ¢é导è´éå¤å建 |
| | | _ = Task.Delay(1000).ContinueWith(_ => |
| | | { |
| | | lock (_globalLocker) |
| | | { |
| | | // ä»
å½éæªè¢«éæ°ææä¸å·²éæ¾æ¶æ¸
ç |
| | | if (_resourceLocks.TryGetValue(resourceId, out var meta) |
| | | && meta.IsReleased |
| | | && meta.HoldingThreadId == -1) |
| | | { |
| | | _resourceLocks.TryRemove(resourceId, out var _resid); |
| | | } |
| | | } |
| | | }); |
| | | } |
| | | } |
| | | |
| | | /// <summary> |
| | | /// æ£æ¥èµæºæ¯å¦è¢«éå® |
| | | /// </summary> |
| | | public static bool IsLocked(string resourceId) |
| | | { |
| | | if (!_resourceLocks.TryGetValue(resourceId, out var meta)) |
| | | return false; |
| | | |
| | | return !meta.IsReleased && meta.HoldingThreadId != -1; |
| | | } |
| | | |
| | | public static void TestUsed() |
| | | { |
| | | string orderNo = "testt"; |
| | | bool lockAcquired = false; |
| | | try |
| | | { |
| | | // å°è¯è·åéï¼èªå¨3-5ç§è¶
æ¶ï¼ |
| | | lockAcquired = MemoryLockManager.TryAcquireLock(orderNo); |
| | | if (lockAcquired) |
| | | { |
| | | // æ§è¡ä¸å¡é»è¾ï¼å¦å¤ç订åï¼ |
| | | Console.WriteLine($"线ç¨[{Thread.CurrentThread.ManagedThreadId}]è·åéï¼{orderNo}"); |
| | | // 模æä¸å¡èæ¶ï¼æµè¯è¶
æ¶éæ¾ï¼ |
| | | // Thread.Sleep(6000); |
| | | } |
| | | else |
| | | { |
| | | Console.WriteLine($"èµæº[{orderNo}]被å ç¨ï¼è·åé失败"); |
| | | } |
| | | } |
| | | catch (Exception ex) |
| | | { |
| | | Console.WriteLine($"ä¸å¡å¤çå¼å¸¸ï¼{ex.Message}"); |
| | | } |
| | | finally |
| | | { |
| | | // éæ¾éï¼ä»
彿åè·åæ¶ï¼ |
| | | if (lockAcquired) |
| | | { |
| | | try |
| | | { |
| | | MemoryLockManager.ReleaseLock(orderNo); |
| | | Console.WriteLine($"线ç¨[{Thread.CurrentThread.ManagedThreadId}]éæ¾éï¼{orderNo}"); |
| | | } |
| | | catch (Exception ex) |
| | | { |
| | | Console.WriteLine($"éæ¾é失败ï¼{ex.Message}"); |
| | | } |
| | | } |
| | | } |