問題的提出
這幾天一直在折騰的數據交換的例子中,我們使用互斥量來保證線程間數據交換的完整性。不難看出,要保證數據交換的正常進行,需要使用數據類的程序的設計者理解線程間數據交換的機制。
C++的第一個特性就是封裝,封裝通過分離接口和實現,除了降低模塊之間耦合性以外,還可以使功能的利用者在不瞭解功能實現細節的情況使用該功能。
本文就利用這個特性將多線程數據保護功能封裝在數據類中,以實現多任務安全的數據類。
代碼
首先來看頭文件,唯一明顯的不同是,增加了QMultex成員和相應的頭文件。
#ifndef DATAARRAY_H
#define DATAARRAY_H
#include <qmutex.h>
#define ARRAY_SIZE 500
class DataArray
{
protected:
int m_buffer[ARRAY_SIZE];
int m_dataSize;
QMutex m_mutex;
public:
DataArray();
int getDataSize();
int getData(int index);
int addData(int data);
void clearData();
int setData(int* buffer, int data_size);
int removeData(int* buffer,
int buffer_size);
};
#endif // DATAARRAY_H
再看CPP。
#include "dataarray.h"
DataArray::DataArray()
:m_dataSize(0)
,m_mutex(QMutex::Recursive)
{
}
int DataArray::getDataSize()
{
m_mutex.lock();
int size = 0;
size = m_dataSize;
m_mutex.unlock();
return size;
}
int DataArray::getData(int index)
{
m_mutex.lock();
int result = -1;
if(index >= 0 && index < m_dataSize)
{
int data = m_buffer[index];
result = data;
}
m_mutex.unlock();
return result;
}
int DataArray::addData(int data)
{
m_mutex.lock();
if(m_dataSize < (ARRAY_SIZE - 1))
{
m_buffer[m_dataSize] = data;
m_dataSize++;
}
m_mutex.unlock();
return true;
}
void DataArray::clearData()
{
m_mutex.lock();
m_dataSize = 0;
m_mutex.unlock();
}
int DataArray::setData(int* buffer,
int data_size)
{
m_mutex.lock();
int set_count = 0;
if(getDataSize() == 0)
{
for(int i = 0; i < data_size; i++)
{
addData(buffer[i]);
}
set_count = data_size;
}
m_mutex.unlock();
return set_count;
}
int DataArray::removeData(int* buffer,
int buffer_size)
{
m_mutex.lock();
int remove_count = 0;
int data_size = getDataSize();
if(buffer_size >= data_size)
{
for(int i = 0; i < data_size; i++)
{
buffer[i] = getData(i);
}
clearData();
remove_count = data_size;
}
m_mutex.unlock();
return remove_count;
}
代 碼略長,但應該很好理解,這裏爲了實現多任務安全做的事情很簡單:在每個成員函數的最開始調用m_mutex.lock();在函數返回之前調用 m_mutex.unlock()。這樣做就可以保證數據操作的代碼不會被途中打斷。所有的一切都在類的內部實現,不用利用者操心。
遞歸互斥量
不 知道你注意了沒有,最後的兩個成員函數:setData和removeData是新增加的,在調用了m_mutex.lock()之後,又調用了其他成員 函數(例如getDataSize)。在這些成員函數中還會再次調用m_mutex.lock(),如果按照一般的邏輯來說,第二次調用應該不會成功。如果沒有特別的方法的話,恐怕就要通過調整代碼結構來解決了。
這 裏請你回頭看構造函數,在初始化QMutex時,使用了QMutex::Recursive參數。使用這個參數值初始化QMutex以後,在同一個線程中 重複調用lock方法時總會成功,不同線程調用lock方法則遵循一般的原則。這樣就用最簡單的方式解決了互斥量加鎖嵌套的問題,就像什麼都沒有發生一 樣。
當然,lock/unlock的配對執行總是必須的。
寫在文章的最後
既然已經讀到這裏了,拜託大家再用一分鐘時間,將文章轉發到各位的朋友圈,微信羣中。