#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 - захваченных блокировок нет и поток не находится внутри какого-либо блока
Комментариев нет:
Отправить комментарий