多线程函数中的 LockRecursionException

LockRecursionException in multiple threaded function

我不知道如何描述它,但我得到一个例外,当代码写得很好时,应该没有位置。这个异常是关于 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函数后马上就释放了锁

How can you see I'm releasing lock right after entering XAxysScan function.

ReaderWriterLockSlim is a synchronization object允许多个线程从资源读取,但只允许 1 个资源写入(理想情况下)。

这很重要的原因是因为 ReaderWriterLockSlim 实现这种效果的具体方法需要称为 Managed Thread Affinity 的东西,这基本上意味着无论 Task 还是 Thread调用 EnterReadLock() 的必须是 same Task 或调用 ExitReadLock();.

的线程

当我们看下面的时候,我们可以看到你已经RunTasks(ScreenScanning ss)进入了锁,但是你马上开始了一个新的child Task并且通过了ReaderWriterLockSlim作为对 XAxysScan().

的参考
ScreenLocker.EnterReadLock();
var t = new Task(() =>
{
    PosXOfBlocksAndMeaningOfIt.Add(ss.XAxysScan(PosYOfRowsToScan[i], Screen, ref ScreenLocker));
});

只有进入锁的 Task 才能释放锁。至少对于使用 Managed Thread Affinity.

ReaderWriterLockSlim 之类的同步 objects

考虑将 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();
    }