AutoResetEvent вызывает взаимоблокировку в многопоточном приложении UCMA 3.0

В моем цикле Parallel.Foreach я звоню

              _helper.subscribeUserEndPoint(loop._contactGrpSvcs);

_helper — это класс инкапсуляции для UserEndPoint и всех других операций, таких как подписка.

Методы подписки:

        public void subscribeUserEndPoint(ContactGroupServices cntGrpSvcs)
        {

            cntGrpSvcs.BeginSubscribe(TerminateSubscribe, cntGrpSvcs);

            _contactSubscribeCompleted.WaitOne();

            LOG.Info("Returning from Successful Subscribe Endpoint");
        }


    private void TerminateSubscribe(IAsyncResult result)
    {

        ContactGroupServices cntGrpSvcs = result.AsyncState as ContactGroupServices;
        try
        {
            cntGrpSvcs.EndSubscribe(result);
        }
        catch (Exception ex)
        {
            LOG.Error("Failed to Complete Subscribe. " + ex.StackTrace);
        }
        CollaborationSubscriptionState state = cntGrpSvcs.CurrentState;
        LOG.Info("Subscribed State = " + state.ToString());

            _contactSubscribeCompleted.Set();

    }

Взаимная блокировка потоков при ожидании _contactSubscribeCompleted.WaitOne(); Как избежать этой тупиковой ситуации?

Ваше здоровье,

  -- Brian

PS: Одной из причин, по которой могут возникать взаимоблокировки, является внутренняя проблема с AutoResetEvent. Из документации: «Нет гарантии, что каждый вызов метода Set освобождает поток. Если два вызова слишком близки друг к другу, так что второй вызов происходит до того, как поток будет освобожден, освобождается только один поток. Это как если бы второй вызов не происходил. Кроме того, если Set вызывается, когда нет ожидающих потоков, а AutoResetEvent уже сигнализируется, вызов не имеет никакого эффекта." Есть ли обходной путь для этого ??


person Brian Antao    schedule 06.06.2012    source источник
comment
Это не тупик. Тупик возникает, когда двум потокам требуется доступ к ресурсу, заблокированному другим. Это не тот случай здесь   -  person Panagiotis Kanavos    schedule 06.06.2012
comment
что заставляет вас думать, что это тупик, а не просто блок? чтобы понять взаимоблокировку, нам нужно понять взаимодействие между двумя потоками. Я не вижу способа зайти в тупик, поскольку TerminateSubscribe никогда не пытается взять конкурирующую блокировку.   -  person Marc Gravell    schedule 06.06.2012
comment
Где вызов TerminateSubscribe? Вызывает ли BeginSubscribe когда-либо TerminateSubscribe?   -  person Panagiotis Kanavos    schedule 06.06.2012
comment
Как указывает Марк, вполне возможно, что это просто блок из-за двух наборов AutoResetEvent из двух потоков, близких друг к другу, которые переходят к WaitOne одного потока, а второй остается в ожидании.   -  person Brian Antao    schedule 06.06.2012
comment
зачем вообще использовать здесь дескриптор ожидания, нельзя ли просто сделать cntGrpSvcs.EndSubscribe(cntGrpSvcs.BeginSubscribe(null, null))? (плюс некоторая обработка/регистрация исключений)   -  person Mike Zboray    schedule 07.06.2012


Ответы (1)


Вызывая метод begin и затем ожидая завершения операции, вы вызываете API полностью синхронно. Это проще сделать без ручек ожидания. Вы можете просто связать методы начала и конца вместе.

public void subscribeUserEndPoint(ContactGroupServices cntGrpSvcs)
{
  try
  {
    cntGrpSvcs.EndSubscribe(cntGrpSvcs.BeginSubscribe(null, null));
    CollaborationSubscriptionState state = cntGrpSvcs.CurrentState;
    LOG.Info("Subscribed State = " + state.ToString());
    LOG.Info("Returning from Successful Subscribe Endpoint");
  }
  catch (Exception ex)
  {
    LOG.Error("Failed to Complete Subscribe. Exception: " + ex.ToString());
  }   
}
person Mike Zboray    schedule 07.06.2012