1

我不知道如何描述它,但是当代码写得很好时,我得到了一个例外。此异常与 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 函数后立即释放锁定。

4

1 回答 1

2

你怎么能看到我在进入 XAxysScan 函数后立即释放锁定。

ReaderWriterLockSlim是一个同步对象,它允许多个线程从一个资源中读取,但只允许一个资源写入它(理想情况下)。

之所以重要,是因为ReaderWriterLockSlim实现这种效果的具体方式,需要一个叫做Managed Thread Affinity的东西,这基本上意味着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这样的同步对象使用Managed Thread Affinity

考虑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();
    }
于 2021-05-16T18:52:29.703 回答