繁体   English   中英

我的循环缓冲区线程安全吗? 如果没有,我该怎么做?

[英]Is my circular buffer threadsafe? If not, how can I make it so?

我创建了一个循环缓冲区类,需要从两个不同的线程访问它。 循环缓冲区使用二维数组,其中一维为行数,另一维为行数数组(其中2048个)的元素。 用户界面线程可以在任何给定时间从数组读取所有行。 后台线程是一个TCP服务器线程,它检索需要在此数组中插入的2048个浮点数。 这是代码

    static class CircularArrayBuffer
    {

    static float[,] buffer;
    static int columns, rows;
    static int nextFree = 0;

    public static void CreateBuffer(int _columns, int _rows)
    {
        columns = _columns;
        rows = _rows;

        buffer = new float[rows,columns];
        nextFree = 0;       //reset pointer to first free buffer
    }

    public static float[] GetData(int index)
    {
        if (index > rows)
        {
            throw new System.ArgumentException("Index cannot be more than rows", "index");
        }

        float[] rowArray = new float[columns];

        Buffer.BlockCopy(buffer, (((nextFree - 1 + index) % rows) * 4 * columns), rowArray, 0, columns * 4); //takes 2 microseconds!

        return rowArray;
    }       

    public static void  AddData(float[] rowArray) //number of columns must be set!
    {
        if (rowArray.Count() > columns)
        {
            throw new System.ArgumentException("Data length cannot be more than number of columns", "columns");
        }

        Buffer.BlockCopy(rowArray, 0, buffer, nextFree * 4 * columns, columns * 4);
        nextFree = (nextFree + 1) % rows;
    }
}

因此,用户界面线程将每隔50毫秒左右检索一次所有行,而后台TCP服务器将每隔50毫秒左右检索一次行。 用户界面线程实际上是OpenGL的OnRender回调。 我会在这堂课上遇到问题吗? 如果可以,我该如何避免呢? 谢谢汤姆

如果写入发生在GetData()的中间,则总是有可能获得不一致的值。 您可能希望使用锁来使Getdata和Adddata操作原子化以获得一致的值。 还要注意,它现在可能还不错,但是由于值得一提,使Get调用异步以避免在不立即获得锁的情况下冻结UI线程始终是一件好事。

对我来说,它看起来并不安全。 您不妨尝试这样的方法:

static class CircularArrayBuffer
{

    static float[,] buffer;
    static int columns, rows;
    static int nextFree = 0;
    static readonly ReaderWriterLockSlim rwLockSlim = new ReaderWriterLockSlim();

    public static void CreateBuffer(int _columns, int _rows)
    {
        columns = _columns;
        rows = _rows;

        buffer = new float[rows, columns];
        nextFree = 0;       //reset pointer to first free buffer
    }

    public static float[] GetData(int index)
    {
        try
        {
            rwLockSlim.EnterReadLock();
            if (index > rows)
            {
                throw new System.ArgumentException("Index cannot be more than rows", "index");
            }

            float[] rowArray = new float[columns];

            Buffer.BlockCopy(buffer, (((nextFree - 1 + index) % rows) * 4 * columns), rowArray, 0, columns * 4); //takes 2 microseconds!
        }
        catch(Exception ex)
        {
            //handle the exception nicely
        }
        finally
        {
            rwLockSlim.ExitReadLock();
        }
        return rowArray;
    }

    public static void AddData(float[] rowArray) //number of columns must be set!
    {
        try
        {
            rwLockSlim.EnterWriteLock();
            if (rowArray.Count() > columns)
            {
                throw new System.ArgumentException("Data length cannot be more than number of columns", "columns");
            }

            Buffer.BlockCopy(rowArray, 0, buffer, nextFree * 4 * columns, columns * 4);
            nextFree = (nextFree + 1) % rows;
        }
        catch(Exception ex)
        {
            //handle the exception nicely
        }
        finally
        {
            rwLockSlim.ExitWriteLock();
        }
    }
}

这就是为什么

  • 没有什么可以阻止其他线程从AddData修改nextFree同时GetData线程试图使用它
  • 多个线程可以访问AddData并修改nextFree因此Buffer.BlockCopy会将数据放置在错误的索引处

暂无
暂无

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM