Страницы

Поиск по вопросам

четверг, 9 апреля 2020 г.

Что не так с определением дедлока?

#net #многопоточность #vbnet

                    
Код из вопроса Насколько случайны данные, сгенерированные таким образом? упал:



Т. е. в одном из методов произошёл вызов Release семафора, когда семафор был свободен.

Imports System.Threading

Module All
  Dim Value1 As Integer, Value2 As Integer
  Dim Sem1 As New SemaphoreSlim(1, 1), Sem2 As New SemaphoreSlim(1, 1)
  Dim Count As Integer

  Private Sub Inc(SemA As SemaphoreSlim, SemB As SemaphoreSlim, ByRef Value As Integer)
    Do
      Thread.Sleep(0)
      SemA.Wait()
      Interlocked.Increment(Count)
      SemB.Wait()
      Interlocked.Decrement(Count)
      Interlocked.Increment(Value)
      If SemB.CurrentCount = 0 Then SemB.Release() Else Interlocked.Increment(Count)
      If SemA.CurrentCount = 0 Then SemA.Release() Else Interlocked.Increment(Count)
    Loop
  End Sub

  Private Sub Init()
    Call (New Thread(Sub() Inc(Sem1, Sem2, Value1))).Start()
    Call (New Thread(Sub() Inc(Sem2, Sem1, Value2))).Start()
  End Sub

  Public Function GetRandBit() As Integer
    If Thread.VolatileRead(Count) = 2 Then
      Thread.VolatileWrite(Value1, 0)
      Thread.VolatileWrite(Value2, 0)
      Interlocked.Decrement(Count)
      Sem2.Release()
    End If

    Do Until Thread.VolatileRead(Count) = 2
      Thread.Sleep(16)
    Loop

    Return Thread.VolatileRead(Value1) And 1
  End Function

  Sub Main()
    Init()
    Do
      Console.Write(GetRandBit())
    Loop
  End Sub
End Module


Указанный в исключении метод _Lambda$__7-1 это метод второго потока:

[SpecialName]
internal void _Lambda\u0024__7\u002D1()
{
  All.Inc(All.Sem2, All.Sem1, ref All.Value2);
}


Из-за чего могла произойти такая ошибка?

Насколько я представляю, Sem2.Release() из GetRandBit вызывается только если оба
потока захватили по первой блокировке и никак не может возникнуть между проверкой на
количество блокировок и вызовом Release. На момент вызова Release одним из двух потоков,
он владеет обеими блокировками и никто посторонний семафоры не трогает. Что могло пойти
не так в этой схеме?
    


Ответы

Ответ 1



Похоже, всё-таки разобрался. Последовательность такая: Поток 2 владеет блокировкой 2 (1/2) Поток 1 владеет блокировкой 1 (1/2) Возникает дедлок (1/2) Управляющий поток снимает блокировку 2 (1/*) Поток 1 захватывает блокировку 2 (12/*) Поток 1 выполняет инкремент (12/*) Поток 1 снимает блокировку 2 (1/*) Поток 1 снимает блокировку 1 (0/*) Поток 2 захватывает блокировку 1 (0/*1) Поток 2 выполняет инкремент (0/*1) Поток 2 снимает блокировку 1 (0/*) Поток 1 захватывает блокировку 1 (1/*) Поток 1 захватывает блокировку 2 (12/*) Поток 2 проверяет наличие блокировки 2 (12/*) Блокировка есть, но поток 2 не знает, что она чужая Поток 1 делает инкремент (12/*) Поток 1 снимает блокировку 2 (1/*) Поток 2 пытается снять блокировку 2 и падает В скобках обозначено владение блокировками: 1 - захвачена блокировка 1 и поток находится внутри её блока 2 - захвачена блокировка 2 и поток находится внутри её блока * - блокировка 2 НЕ захвачена, но поток находится внутри её блока 0 - захваченных блокировок нет и поток не находится внутри какого-либо блока

Комментариев нет:

Отправить комментарий