EA&UML日拱一卒-多任務編程超入門-(8)多任務安全的數據類

問題的提出


這幾天一直在折騰的數據交換的例子中,我們使用互斥量來保證線程間數據交換的完整性。不難看出,要保證數據交換的正常進行,需要使用數據類的程序的設計者理解線程間數據交換的機制。


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的配對執行總是必須的。


寫在文章的最後


既然已經讀到這裏了,拜託大家再用一分鐘時間,將文章轉發到各位的朋友圈,微信羣中。


本公共號的成長需要您的支持!
閱讀更多更新文章,請掃描下面二維碼,關注微信公衆號【面向對象思考】