多任務(wù)編程超入門-多任務(wù)安全的數(shù)據(jù)類
問題的提出
這幾天一直在折騰的數(shù)據(jù)交換的例子中,我們使用互斥量來保證線程間數(shù)據(jù)交換的完整性。不難看出,要保證數(shù)據(jù)交換的正常進(jìn)行,需要使用數(shù)據(jù)類的程序的設(shè)計者理解線程間數(shù)據(jù)交換的機制。
C++的第一個特性就是封裝,封裝通過分離接口和實現(xiàn),除了降低模塊之間耦合性以外,還可以使功能的利用者在不了解功能實現(xiàn)細(xì)節(jié)的情況使用該功能。
本文就利用這個特性將多線程數(shù)據(jù)保護(hù)功能封裝在數(shù)據(jù)類中,以實現(xiàn)多任務(wù)安全的數(shù)據(jù)類。
代碼
首先來看頭文件,唯一明顯的不同是,增加了QMultex成員和相應(yīng)的頭文件。
#ifndef?DATAARRAY_H #define?DATAARRAY_H
#include
#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;
}代 碼略長,但應(yīng)該很好理解,這里為了實現(xiàn)多任務(wù)安全做的事情很簡單:在每個成員函數(shù)的最開始調(diào)用m_mutex.lock();在函數(shù)返回之前調(diào)用 m_mutex.unlock()。這樣做就可以保證數(shù)據(jù)操作的代碼不會被途中打斷。所有的一切都在類的內(nèi)部實現(xiàn),不用利用者操心。
遞歸互斥量
不 知道你注意了沒有,最后的兩個成員函數(shù):setData和removeData是新增加的,在調(diào)用了m_mutex.lock()之后,又調(diào)用了其他成員 函數(shù)(例如getDataSize)。在這些成員函數(shù)中還會再次調(diào)用m_mutex.lock(),如果按照一般的邏輯來說,第二次調(diào)用應(yīng)該不會成功。如果沒有特別的方法的話,恐怕就要通過調(diào)整代碼結(jié)構(gòu)來解決了。
這 里請你回頭看構(gòu)造函數(shù),在初始化QMutex時,使用了QMutex::Recursive參數(shù)。使用這個參數(shù)值初始化QMutex以后,在同一個線程中 重復(fù)調(diào)用lock方法時總會成功,不同線程調(diào)用lock方法則遵循一般的原則。這樣就用最簡單的方式解決了互斥量加鎖嵌套的問題,就像什么都沒有發(fā)生一 樣。
當(dāng)然,lock/unlock的配對執(zhí)行總是必須的。
寫在文章的最后
既然已經(jīng)讀到這里了,拜托大家再用一分鐘時間,將文章轉(zhuǎn)發(fā)到各位的朋友圈,微信群中。





