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


写在文章的最后


既然已经读到这里了,拜托大家再用一分钟时间,将文章转发到各位的朋友圈,微信群中。


本公共号的成长需要您的支持!
阅读更多更新文章,请扫描下面二维码,关注微信公众号【面向对象思考】
EA&UML日拱一卒-多任务编程超入门-(3)线程协作的理想和现实