当在不同的索引上写入时,这个矩阵类线程安全吗?

Is this matrix class thread safe when writing on different indices?

本文关键字:线程 安全 索引      更新时间:2023-10-16

您可能注意到这后面有一个双*存储。任何读/写的值都被转换成一个平面索引,因此它被存储在一个大的一维数组中。

我需要知道我是否可以在不同线程中对该类型对象的不同索引进行写入?我使用运算符()来设置值。边界是在实例化对象时预定义的。即:矩阵m(行,cols);我在boost线程中使用这个并有问题。我需要放置任何作用域锁吗?但我相信那将是性能上的损失。

class matrix
{
public:
    matrix(size_t rows, size_t cols):_rows(rows),_cols(cols),_data(0)
    {
        reset(rows,  cols);
    }
    matrix():_data(0),_rows(0),_cols(0){}
    ~matrix()
    {
        if (0 != _data)
        {
            delete [] _data;
        }
    }
    matrix(const matrix& copythis)
        :_data(0),_rows(0),_cols(0)
    {
        if (0 != _data)
        {
            delete [] _data;
        }
        _rows = copythis.rows();
        _cols = copythis.cols();
        _data = new double [_rows * _cols];
        if (0 == _data)
        {
            NL_THROW("Insufficient memory to create a cloned matrix of double of " << _rows << " X " << _cols);
        }
        memcpy(_data, copythis._data, _rows * _cols * sizeof(double) );
    }

public:
    const matrix& operator = (const matrix& copythis)
    {
        if (0 != _data)
        {
            delete [] _data;
        }
        _rows = copythis.rows();
        _cols = copythis.cols();
        _data = new double [_rows * _cols];
        if (0 == _data)
        {
            NL_THROW("Insufficient memory to create a cloned matrix of double of " << _rows << " X " << _cols);
        }
        memcpy(_data, copythis._data, _rows * _cols * sizeof(double) );
        return (*this);
    }



    double* cArray()
    {
        if (0 == _data)
        {
            NL_THROW("Matrix is not initialised");
        }
        return _data;
    }

    void reset(size_t rows, size_t cols)
    {
        if (0 != _data)
        {
            delete [] _data;
        }
        _rows = rows;
        _cols = cols;
        _data = new double[rows * cols];
        if (0 == _data)
        {
            NL_THROW("Insufficient memory to create a matrix of double of " << _rows << " X " << _cols);
        }
        memset(_data, 0, sizeof(double) * _rows * _cols);
    }


    double& operator () (size_t rowIndex, size_t colIndex) const
    {
        if (rowIndex >= _rows)
        {
            NL_THROW("Row index " << rowIndex << " out of range(" << _rows - 1 << ")");
        }
        if (colIndex >= _cols)
        {
            NL_THROW("Column index " << colIndex << " out of range(" << _cols - 1 << ")");
        }
        size_t flatIndex = colIndex + rowIndex * _cols;
        return _data[flatIndex];
    }

    Array rowSlice(size_t rowIndex) const
    {
        if (rowIndex >= _rows)
        {
            NL_THROW("Cannot slice matrix, required row: " << rowIndex << " is out of range(" << (_rows - 1) << ")");
        }
        Array retval(_data + rowIndex * _cols, _cols);
        /*
        for(size_t i = 0; i < _cols; i++)
        {
            retval[i] = operator()(rowIndex, i);
        }
        */
        return retval;
    }

    Array colSlice(size_t colIndex) const
    {
        if (colIndex >= _cols)
        {
            NL_THROW("Cannot slice matrix, required row: " << colIndex << " is out of range(" << (_cols - 1) << ")");
        }
        Array retval(_rows);
        for(size_t i = 0; i < _rows; i++)
        {
            retval[i] = operator()(i, colIndex);
        }
        return retval;
    }

    void fill(double value)
    {
        for(size_t rowIndex = 0; rowIndex < _rows; rowIndex++)
        {
            for(size_t colIndex = 0; colIndex < _cols; colIndex++)
            {
                size_t flatIndex = colIndex + rowIndex * _cols;
                _data[flatIndex] = value;
            }
        }
    }

    bool isEmpty() const
    {
        if (0 == _rows) return true;
        if (0 == _cols) return true;
        return false;
    }
    size_t rows() const {return _rows;}
    size_t cols() const {return _cols;}
private:
    double* _data;
    size_t _rows;
    size_t _cols;
};

如果您能保证所有的线程永远不会在矩阵的同一元素上发生冲突,那么您就不需要锁定。在实践中,期望这样的保证是不现实的,因此需要锁定。

这个类的编写方式,除了getter rows()cols(),您需要在上锁定每个方法,几乎在每个方法上尽可能广泛的范围。

您甚至不能依赖于size_t类型是原子的,因为reset()以非原子的方式设置_row_col。任何需要知道_row_col的操作,例如isEmpty(),如果从另一个线程调用reset(),将会遇到麻烦。