2014-03-03 122 views
-1

看來,鎖在我的代碼下面不起作用。爲什麼我的鎖不會阻塞?

這裏有什麼,我試圖做一些背景:

我有一個庫存管理系統,該系統具有以下功能:

  1. 接收訂單
  2. 分配現有庫存來履行訂單
  3. 如果無法填寫訂單,則需要延期交貨
  4. 嘗試在添加新庫存時填寫缺貨訂單
  5. 允許取消訂單。

因此,假設有100個訂單處於缺貨狀態,那麼一批庫存即可滿足所有100個缺貨訂單。當我的Warehousing類收到貨物(下面未顯示)時,將調用下面定義的AllocateOutstandingRequests方法。最終,我們在下面的AllocateInventory方法的第一個foreach循環中結束,它遍歷100個訂單,試圖爲每個訂單分配庫存。現在,假設我的消息傳遞類(部分定義如下)接收到該列表的訂單號2的CancelOrder,則循環在項80上。 CancelOrder例程在Allocation類中調用我的AddCancel例程,並且應該鎖定在鎖上(_cancelsLock),但它不會。我使用timestamps和threadId跟蹤了所有這些代碼,並且我甚至在循環中放置了3秒的延遲,試圖爲延期交付的項目分配庫存。我可以在跟蹤中看到循環完全按照預期工作,每次分配之間有3秒鐘,但是我還可以看到AddCancel調用立即返回到Messaging類,沒有任何延遲;我預計會有延遲,它應該阻止,直到所有100個訂單被分配並鎖定釋放。我究竟做錯了什麼?似乎AddCancel中的鎖並沒有做任何事情!

我已經在代碼中添加了日誌的樣子,以及日誌文件中的實際日誌條目。可以看出,Messaging.ReceiveInventory [threadId 38]接收庫存並調用Allocation.AllocateOutstandingRequests,進而啓動任務[threadId 26]:

2014-02-28 17:00:08,871 [38] INFO - 由用戶處理傳入ReceiveDrugs請求:WELLDYNERX \ privera

2014年2月28日17:00:08871 [38] INFO - NDC 00002323230加到庫存請求者110

2014年2月28日17:00 :08,871 [26]信息 - ...分配未決請求

2014-02-28 17:00:08,887 [26]信息 - 試圖分配100個未完成請求

2014年2月28日17:00:08934 [26] INFO - 延期交貨RequestUID 100689分配

2014年2月28日17:00:23934 [26] INFO - 延期交貨RequestUID 100690分配

2014-02-28 17:00:25,293 [42] INFO - 處理傳入的CancelNotification; UID:100689,來自PRIVERA for RequestorUID:110

2014-02-28 17:00:25,309 [42] INFO - 取消通知UID:100689,處理。

2014年2月28日17:00:39012 [26] INFO - 延期交貨RequestUID 100691分配

2014年2月28日17:00:54012 [26] INFO - 延期交貨RequestUID 100692分配

配置類別:

public static class Allocation 
{ 
    public static void AllocateOutstandingRequests() 
    { 
     var factory = new TaskFactory(_orderedTaskScheduler); 
     TaskScheduler.UnobservedTaskException += OrderedTaskScheduler_UnobservedTaskException; 

     factory.StartNew(() => 
     { 
      Trace.TraceInformation("...Allocating outstanding requests"); 
      List<QueueingRequest> backorderedRequests = InventoryDao.GetBackorderedRequests(); 
      List<AllocationRequest> backorderedRequestsAllocated = 
       AllocateInventory(backorderedRequests.OrderBy(r => r.RequestUID).ToList()); 
      SendAllocationResponses(backorderedRequestsAllocated); 
      Trace.TraceInformation("Completed allocating outstanding requests..."); 
     }); 
    } 

    static List<AllocationRequest> AllocateInventory(List<QueueingRequest> outstandingRequests) 
    { 
     List<AllocationRequest> allocatedBackorderedRequests = new List<AllocationRequest>(); 

     lock (_cancelsLock) 
     { 
      Trace.TraceInformation(string.Format("Attempting to allocate {0} outstanding requests", outstandingRequests.Count)); 

      foreach (QueueingRequest queuedRequest in outstandingRequests) 
      { 
       if (_cancels.Contains(queuedRequest.RequestUID)) continue; 

       AllocationRequest allocationRequest = new AllocationRequest(queuedRequest); 
       if (AllocateOrder(allocationRequest)) 
       { 
        Trace.TraceInformation(string.Format("Backordered RequestUID {0} Allocated", queuedRequest.RequestUID)); 
        allocatedBackorderedRequests.Add(allocationRequest); 
       } 

       for (int iSleepAlot = 0; iSleepAlot < 5; iSleepAlot++) 
        System.Threading.Thread.Sleep(3000); 
      } 

      // Check to see if a CancelOrder came thru for backordered requests 
      // that the code above allocated inventory for. 
      foreach (int requestUID in allocatedBackorderedRequests.Select(r => r.RequestUID)) 
      { 
       if (_cancels.Contains(requestUID)) 
        _cancels.Remove(requestUID); 
      } 
     } 

     return allocatedBackorderedRequests; 
    } 

    static bool AllocateOrder(AllocationRequest request) 
    { 
     bool inventoryAllocated = false; 

     try 
     { 
      if (InventoryDao.SaveAllocation(request)) 
       inventoryAllocated = Warehousing.AllocateDrugs(request.RequestorUID, request.Items); 
     } 
     catch (RequestAlreadyAllocatedException ex) 
     { 
      inventoryAllocated = true; 
     } 
     catch (Exception ex) 
     { 
      Trace.TraceError(ex.ToString()); 
      throw; 
     } 

     return inventoryAllocated; 
    } 

    public static bool AddCancel(int requestUID) 
    { 
     bool requestStatusChangedToAllocated = false; 
     _cancels.Add(requestUID); 

     // block until backordered requests are allocated. 
     lock (_cancelsLock) 
     { 
      requestStatusChangedToAllocated = !_cancels.Contains(requestUID); 

      if (!requestStatusChangedToAllocated) 
       _cancels.Remove(requestUID); 
     } 

     return requestStatusChangedToAllocated; 
    } 

    static readonly TaskScheduler _orderedTaskScheduler = new LimitedConcurrencyLevelTaskScheduler(1); 
    static readonly List<int> _cancels = new List<int>(); 
    static readonly object _cancelsLock = new object(); 
} 

消息類:

public static class Messaging 
{ 
    public static void CancelOrder(CancelNotification notification) 
    { 
     Trace.TraceInformation(string.Format("Processing incoming CancelNotification; UID:{0}, from {1} for RequestorUID:{2}", 
               notification.RequestUID, 
               notification.User, 
               notification.RequestorUID)); 
     // This is a blocking call which returns after all backordered requests are processed. 
     // The call may change the status from backordered to allocated, in which case, we'll 
     // have to DeAllocateDrugs in the services cache 
     bool requestStatusChangedToAllocated = Allocation.AddCancel(notification.RequestUID); 

     // do some work 

     Trace.TraceInformation(string.Format("CancelNotification for UID:{0}, processed.", notification.RequestUID)); 
    } 

    public static List<string> ReceiveInventory(List<ReceivedInventory> received, string user, string comment) 
    { 
     Trace.TraceInformation(string.Format("Processing incoming ReceiveDrugs request by User:{0}", user)); 

     foreach (ReceivedInventory inventory in received) 
     { 
      // do some work 
      Trace.TraceInformation(string.Format("NDC {0} added to inventory for requestor {1}", drugInventory.NDC, inventory.RequestorUID)); 
     } 

     // re-evaluate allocations after inventory is loaded 
     Allocation.AllocateOutstandingRequests(); 
    } 
} 
+0

一般 - 不好的風格。使用您自己的任務計劃程序並一次使用1個任務 - 當您獲得大量訂單時,比鎖定更有效。在消息傳遞環境中進行串行處理的服務器要好得多,以確保只有一名工作人員處理消息。 – TomTom

+0

何時/何處是分配的優秀請求()被調用? –

+0

我看不到任何新的線程/任務正在創建....我看到.StartNew()但沒有我在哪裏看到它叫.. –

回答

0

鎖定做塊。我的單元測試使用了錯誤的數據。對不起浪費任何人的時間。

相關問題