我的循环缓冲线程是否安全?如果没有,我怎么能这样做?

时间:2017-01-16 00:17:14

标签: c# multithreading thread-safety

我创建了一个循环缓冲类,需要从两个不同的线程访问它。循环缓冲区使用二维数组,其中一维是行数,另一维是浮点数组的元素(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;
    }
}

因此,用户界面线程将每50ms左右检索所有行,而后台TCP服务器将每50 ms左右添加1行。用户界面线程实际上是OpenGL的OnRender回调。我是否会遇到这堂课的问题?如果是这样我该如何避免呢? 谢谢,汤姆

2 个答案:

答案 0 :(得分:1)

如果写入发生在GetData()的中间,则总是有可能获得不一致的值。您可能希望使用锁来使Getdata和Adddata操作成为原子以获得一致的值。另外在旁注,它现在可能很好,但是因为它值得一提,它总是很好的使得Get调用异步以避免冻结UI线程,以防它没有立即得到锁。

答案 1 :(得分:1)

它对我来说看起来不安全。 您可能希望尝试这样的事情:

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

这就是为什么

  • nextFree中的线程试图使用AddData时,没有任何内容阻止从GetData修改AddData的不同主题
  • 多个线程可以访问nextFree并修改Buffer.BlockCopy,以便applicationWillFinishLaunching将数据放入错误的索引