#c# #multithreading #exception #readerwriterlockslim #readerwriterlock
Вопрос:
Я не знаю, как это описать, но я получаю исключение, в котором не было бы места, когда код написан хорошо. Это исключение связано с проблемой с ReaderWriterLockSlim, и это исключение LockRecursionException; оно отображается в «ScreenLocker.EnterReadLock();» строка. Не могу найти проблему с моим кодом и описанием, что делать или что может быть не так в Интернете, поэтому я пишу этот вопрос здесь и прошу вас всех о помощи. Это код, с которым у меня проблема:
public static List<Dictionary<int, int>> RunTasks(ScreenScanning ss)
{
var listOfTasks = new List<Task>();
List<Dictionary<int, int>> PosXOfBlocksAndMeaningOfIt = new List<Dictionary<int, int>>();
for (var i = 0; i <= BlocksOnYAxisOnScreen; i )
{
ScreenLocker.EnterReadLock();
var t = new Task(() =>
{
PosXOfBlocksAndMeaningOfIt.Add(ss.XAxysScan(PosYOfRowsToScan[i], Screen, ref ScreenLocker));
});
listOfTasks.Add(t);
}
Task.WaitAll(listOfTasks.ToArray());
return PosXOfBlocksAndMeaningOfIt;
}
и это функции, вызываемые этим методом:
public Dictionary<int, int> XAxysScan(int posY, Bitmap screen, ref ReaderWriterLockSlim screenLocker)
{
screenLocker.ExitReadLock();
Dictionary<int, int> partOfMainTable = new Dictionary<int, int>();
partOfMainTable.Add(666, posY); //used in BotViewUpdate in DataToTableInterpreter
for (int i = 0; i <= 1920; i )
{
if (screen.GetPixel(i, posY) == ColorsInRow[0])
{
if (IsFarmable(posY, ColorsInRow, i, screen))
{
partOfMainTable.Add(i, 1);
}
}
else if (IsBackground(BackgroundColors, i, posY, screen))
{
partOfMainTable.Add(i, 0);
}
else
{
partOfMainTable.Add(i, 2);
}
}
return partOfMainTable;
}
Как вы можете видеть, что я снимаю блокировку сразу после входа в функцию XAxysScan.
Ответ №1:
Как вы можете видеть, что я снимаю блокировку сразу после входа в функцию XAxysScan.
ReaderWriterLockSlim
Это объект синхронизации, который позволяет нескольким потокам считывать данные с ресурса, но разрешает запись в него только 1 ресурсу(в идеале).
Причина, по которой это важно, заключается в том, что конкретный способ, ReaderWriterLockSlim
реализуемый для достижения этого эффекта, требует чего-то, называемого сходством управляемых потоков, что в основном означает, что любой Task
или поток, который вызвал EnterReadLock()
, должен быть тем же Task
или вызывающим потоком ExitReadLock();
.
Когда мы смотрим на следующее, мы видим, что вы RunTasks(ScreenScanning ss)
ввели блокировку, но вы сразу же запускаете нового ребенка Task
и передаете его в ReaderWriterLockSlim
качестве ссылки XAxysScan()
.
ScreenLocker.EnterReadLock();
var t = new Task(() =>
{
PosXOfBlocksAndMeaningOfIt.Add(ss.XAxysScan(PosYOfRowsToScan[i], Screen, ref ScreenLocker));
});
Только тот же Task
, кто входит в замок, может освободить этот замок. По крайней мере, для подобных объектов синхронизации ReaderWriterLockSlim
используйте сходство управляемых потоков.
Подумайте о том, чтобы перейти EnterReadLock()
к этому XAxysScan()
методу.
public Dictionary<int, int> XAxysScan(int posY, Bitmap screen, ref ReaderWriterLockSlim screenLocker)
{
screenLocker.EnterReadLock();
try{
Dictionary<int, int> partOfMainTable = new Dictionary<int, int>();
partOfMainTable.Add(666, posY); //used in BotViewUpdate in DataToTableInterpreter
for (int i = 0; i <= 1920; i )
{
if (screen.GetPixel(i, posY) == ColorsInRow[0])
{
if (IsFarmable(posY, ColorsInRow, i, screen))
{
partOfMainTable.Add(i, 1);
}
}
else if (IsBackground(BackgroundColors, i, posY, screen))
{
partOfMainTable.Add(i, 0);
}
else
{
partOfMainTable.Add(i, 2);
}
}
return partOfMainTable;
}
finally
{
// make sure that even if we encounter an error, we still exit the lock so other threads can enter the lock / begin writing
screenLocker.ExitReadLock();
}