Кажется, что в моем приведенном ниже коде блокировка не работает.

Вот некоторая предыстория того, что я пытаюсь сделать:

У меня есть система управления запасами, которая выполняет следующие функции:

  1. получать заказы
  2. распределять существующие запасы для выполнения заказов
  3. если невозможно выполнить заказ, пометьте его как отложенный
  4. пытаться заполнить невыполненные заказы всякий раз, когда добавляется новый инвентарь
  5. разрешить отмену заказа.

Итак, предположим, что 100 заказов находятся в невыполненном заказе, затем приходит партия инвентаря, которая может заполнить все 100 невыполненных заказов. Когда груз получен моим классом складирования (не показан ниже), вызывается метод AllocateOutstandingRequests, определенный ниже. В конце концов мы попадаем в первый цикл foreach моего метода AllocateInventory ниже, который перебирает 100 заказов, пытаясь выделить инвентарь для каждого. Теперь предположим, что этот цикл находится на элементе 80, когда мой класс обмена сообщениями (частично определенный ниже) получает CancelOrder для заказа номер 2 этого списка. Подпрограмма CancelOrder вызывает мою подпрограмму AddCancel в классе Allocation и ДОЛЖНА блокироваться при блокировке (_cancelsLock), но этого не происходит. Я отслеживаю весь этот код с помощью временных меток и threadId, и я даже помещаю 3-секундную задержку в цикл, который пытается выделить инвентарь для отложенных элементов. Я могу видеть в трассировке, что цикл работает точно так, как ожидалось, с 3 секундами между каждым выделением, но я также вижу, что вызов AddCancel немедленно возвращается обратно в мой класс обмена сообщениями без задержки; Я ожидал задержки, он должен заблокироваться, пока все 100 заказов не будут распределены и блокировка не будет снята. Что я делаю неправильно? Кажется, блокировка в AddCancel вообще ничего не делает!

Я добавил, как выглядит ведение журнала, как в коде, так и в фактических записях журнала из файла журнала. Как видно, Messaging.ReceiveInventory [threadId 38] получает инвентарь и вызывает Allocation.AllocateOutstandingRequests, который, в свою очередь, запускает задачу [threadId 26]:

2014-02-28 17: 00: 08,871 [38] ИНФОРМАЦИЯ - Обработка входящего запроса ReceiveDrugs от пользователя: WELLDYNERX \ privera

2014-02-28 17: 00: 08,871 [38] ИНФОРМАЦИЯ - НДЦ 00002323230 добавлен в инвентарь для запрашивающей стороны 110

2014-02-28 17: 00: 08,871 [26] ИНФОРМАЦИЯ - ... Распределение невыполненных запросов

2014-02-28 17: 00: 08,887 [26] ИНФОРМАЦИЯ - Попытка выделить 100 невыполненных запросов

2014-02-28 17: 00: 08,934 [26] ИНФОРМАЦИЯ - Распределенный идентификатор запроса с невыполненным заказом 100689

2014-02-28 17: 00: 23,934 [26] ИНФОРМАЦИЯ - Распределенный идентификатор запроса с невыполненным заказом 100690

2014-02-28 17: 00: 25,293 [42] INFO - обработка входящего уведомления об отмене; UID: 100689, из PRIVERA для RequestorUID: 110

2014-02-28 17: 00: 25,309 [42] ИНФОРМАЦИЯ - Отменить уведомление для UID: 100689, обработано.

2014-02-28 17: 00: 39,012 [26] ИНФОРМАЦИЯ - выделенный UID запроса с невыполненным заказом 100691

2014-02-28 17: 00: 54,012 [26] ИНФОРМАЦИЯ - выделенный UID запроса с невыполненным заказом 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();
    }
}
-1
Paul Rivera 3 Мар 2014 в 19:43
В общем - плохой стиль. Используйте свой собственный планировщик задач и используйте по одной задаче за раз - ПУТЬ более эффективно, чем блокировка, когда вы получаете тонну заказов. Последовательная обработка в среде обмена сообщениями намного лучше serverd, когда с сообщениями работает только один работник.
 – 
TomTom
3 Мар 2014 в 19:47
Когда / где вызывается AllocateOutstandingRequests ()?
 – 
T McKeown
3 Мар 2014 в 19:58
Я не вижу создаваемых новых потоков / задач .... я вижу .StartNew (), но нигде я не вижу, чтобы они вызывались ..
 – 
T McKeown
3 Мар 2014 в 19:59
Если весь этот код выполняется с использованием одного и того же потока, то никакой блокировки не произойдет.
 – 
T McKeown
3 Мар 2014 в 20:00
Allocation.AllocateOutstandingRequests () вызывается в нескольких местах при изменении инвентаря, например, при получении нового инвентаря. Получение инвентаря осуществляется в другом классе.
 – 
Paul Rivera
3 Мар 2014 в 20:01

1 ответ

Лучший ответ

Блокировка действительно блокирует. В моих модульных тестах использовались неверные данные. Извините за потерянное время.

0
Paul Rivera 4 Мар 2014 в 01:56