EA&UML日拱一卒-多任務編程超入門-(3)線程協作的理想和現實

爲什麼需要協作?


一般說來,只要存在多任務,就需要任務之間的協作。這裏的協作包含數據交換和任務同步。


數據交換很簡單,就是進程或線程之間數據的傳遞,可能是一方生成數據,另外一方使用數據;也可能多方生成數據,多方使用數據等。


同步是進程或線程之間的步調的調整,例如通訊線程生成數據以後,控制線程纔開始工作;或者所有線程都結束以後,應用程序進程才結束等。


我們當然希望線程之間的協作是在編程者不知情的情況下實現的,這可以說是多線程變成的理想狀態,但現實呢?


線程間通訊實例


我們用一個例子來說明。這個例子稍微有些複雜,但是難度並不高,安下心來看下去即可。


數據類DataArray


#ifndef DATAARRAY_H
#define
DATAARRAY_H
#define ARRAY_SIZE  500
class DataArray
{
protected
:
   
int m_buffer[ARRAY_SIZE];
   
int m_dataSize;
public
:
   
DataArray();
int getDataSize();
int getData(int index);
int addData(int data);
void clearData();
};
#endif
// DATAARRAY_H


這個類用於數據生成模塊和使用數據模塊之間傳遞數據,除了構造函數以外這個類一共有四個方法:


  1. getDataSize:取得數組中保存數據的數量

  2. getData:指定索引值取數據

  3. addData:在數組的最後添加數據

  4. clearData:清除所有數據。


以下是實現代碼:


#include "dataarray.h"
#include
<iostream>
using namespace std;
DataArray::DataArray()
:m_dataSize(0)
{
}
int DataArray::getDataSize()
{
 
return m_dataSize;
}
int DataArray::getData(int index)
{
 
if(index >= 0 && index < m_dataSize)
 
{
     
return m_buffer[index];
 
}
 
else
 
{
     
return -1;
 
}
}
int DataArray::addData(int data)
{
   
if(m_dataSize < (ARRAY_SIZE - 1))
   
{
       
m_buffer[m_dataSize] = data;
       
m_dataSize++;
   
}
   
return true;
}

void
DataArray::clearData()
{
   
m_dataSize = 0;
}


數據類DataArray的實現非常簡單,大家可以自己看一下。


另外爲了簡化利用側的代碼,另外準備了兩個簡單的C函數。


void WriteData(int base)
{
   
for(int j = 0; j < 5; ++j)
   
{
       
data_array.addData(j);
   
}
}


WriteData連續寫入5個數據,分別是0,1,2,3,4.


void ReadData()
{
   
int data_size = data_array.getDataSize();
   
int total = 0;
   
for(int k = 0; k < data_size; ++k)
   
{
       
total += data_array.getData(k);
   
}
   
cout << "RT:----total="  << total << endl;
}


ReadData從緩衝區中讀出數據並求和.


單任務條件下的執行情況


可以通過一段小程序調用上述兩個函數。


for(int i = 0; i < 10; ++i)    
{
   
cout << "WT:<<<<WriteData:" << i << "<<<<" << endl;
   
WriteData(i);
   
cout << "RT:>>>>ReadData:" << i << ">>>>" << endl;
ReadData();
data_array.clearData();
}


以下是執行結果


WT:<<<<WriteData:0<<<<
RT:>>>>ReadData:0>>>>
RT::----data_size=5
RT:----total=10
WT:<<<<WriteData:1<<<<
RT:>>>>ReadData:1>>>>
RT::----data_size=5
RT:----total=10
WT:<<<<WriteData:2<<<<
RT:>>>>ReadData:2>>>>
RT::----data_size=5
RT:----total=10
WT:<<<<WriteData:3<<<<
RT:>>>>ReadData:3>>>>
RT::----data_size=5
RT:----total=10
WT:<<<<WriteData:4<<<<
RT:>>>>ReadData:4>>>>
RT::----data_size=5
RT:----total=10
WT:<<<<WriteData:5<<<<
RT:>>>>ReadData:5>>>>
RT::----data_size=5
RT:----total=10
WT:<<<<WriteData:6<<<<
RT:>>>>ReadData:6>>>>
RT::----data_size=5
RT:----total=10
WT:<<<<WriteData:7<<<<
RT:>>>>ReadData:7>>>>
RT::----data_size=5
RT:----total=10
WT:<<<<WriteData:8<<<<
RT:>>>>ReadData:8>>>>
RT::----data_size=5
RT:----total=10
WT:<<<<WriteData:9<<<<
RT:>>>>ReadData:9>>>>
RT::----data_size=5
RT:----total=10


可以看到,讀寫操作分別執行10次,每次取得的數據都是5個,求和結果都是10.


多任務條件下的執行情況


示例代碼


//define CreateDataTask class.
class CreateDataTask : public QThread
{
   
void run()
{
for(int i = 0; i < 10; ++i)
{
cout << "WT:<<<<WriteData:" << i << "<<<<" << endl;
           
WriteData(i);
}
}
};
//Create thread object of CreateDataTask.
CreateDataTask *writer = new CreateDataTask();
//Start Thread.
writer->start(QThread::NormalPriority);
for(int i = 0; i < 10; ++i)
{
   
cout << "RT:>>>>ReadData:" << i << ">>>>" << endl;
ReadData();
data_array.clearData();
}


首先定義一個CreateDataTask類,它的成員函數run的內容是執行10次寫數據函數WriteData。這裏只是定義CreateDataTask類,就像定義函數一樣。


接下來創建CreateDataTask對象並執行。start方法被調用之後,CreateDataTask線程開始執行。


最後接下來調用10次讀數據函數,每次調用讀數據函數以後也會清除數據。


以下是程序執行以後的輸出結果:


RT:>>>>ReadData:WT:<<<<WriteData:0<<<<
WT:<<<<WriteData:1<<<<
WT:<<<<WriteData:20>>>>
RT::----data_size=10
RT:----total=20
RT:>>>>ReadData:1>>>>
RT::----data_size=0
RT:----total=0
RT:>>>>ReadData:2>>>>
RT::----data_size=0
RT:----total=0
RT:>>>>ReadData:3>>>>
RT::----data_size=0
RT:----total=0
RT:>>>>ReadData:4>>>>
RT::----data_size=0
RT:----total=0
RT:>>>>ReadData:5>>>>
RT::----data_size=0
RT:----total=0
RT:>>>>ReadData:6>>>>
RT::----data_size=0
RT:----total=0
RT:>>>>ReadData:7>>>>
RT::----data_size=0
RT:----total=0
RT:>>>>ReadData:8>>>>
RT::----data_size=0
RT:----total=0
RT:>>>>ReadData:9>>>>
RT::----data_size=0
RT:----total=0
<<<<
WT:<<<<WriteData:3<<<<
WT:<<<<WriteData:4<<<<
WT:<<<<WriteData:5<<<<
WT:<<<<WriteData:6<<<<
WT:<<<<WriteData:7<<<<
WT:<<<<WriteData:8<<<<
WT:<<<<WriteData:9<<<<


從輸出結果可以看出:讀,寫函數執行的先後順序不定;讀出的數據個數,求和結果有經常會發生錯誤。如果多次執行的話,會的到許多不同的結果。


爲了獲得快速響應而導入了多線程,但是結果並不是我們想要的,這是爲什麼呢?


且聽下回分解。


示例代碼下載鏈接:


本連載示例代碼可以在QT環境下運行,請從以下鏈接下載:

http://download.csdn.net/detail/craftsman1970/9893127


寫在文章的最後


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


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