我是做行业软件的所以经常会接触到对数据进行实时采集的问题。一般的问题是,一个线程读取数据,其他的线程对数据进行处理(如画出实时曲线,将数据存入文件等等)。这涉及到两方面问题.1,线程的同步问题—典型的生产者和消费者的问题;2,数据结构问题。即将采集的数据以什么形式存储并由消费者进行读取。
下面我将对这两个问题和大家一起探讨一下。相信大家对生产者消费者问题并不陌生。在读书的时候我们采用系统体提供的p,v解决,这是对同一临界区资源同时进行读写需要的保护措施,本工程使用缓冲队列,故不需要对临界区进行加锁 。马上我会实现双缓存的版本。在此版本中我会实现对临界区的加减锁。
读取的数据要存储到相应的数据结构中,可以有很多种选择,比如存成任务缓冲队列(以链表实现)其中大部分工程使用循环队列用于一写多读,写线程将数据写入队列,读线程从队列中读取。或者实现双缓存方法,即写线程写满一个缓存读线程来取数据同时写线程将数据写入另一个缓存。
具体的实现如下:
链表的节点声明如下:
typedef struct NODE
{
int Buffer[MAX_BUFFER_SIZE];
long Counters;
bool ReadEnable;
NODE * pNext;
}NODE;
其中数组Buffer[]用来存放采集数据。Counters用来描述有多少个读线程曾对节点进行访问。如果所有读线程都访问过此节点,即可将此节点删除!ReadEnable表示其节点是否可读。
链表的声明如下:
typedef struct LIST
{
NODE * pReadPtr;
NODE * pWritePtr;
NODE * pHeadPtr;
}LIST;
其中pReadPtr为指向可读节点的指针,pWritePtr为指向可写节点的指针。其中pReadPtr在pWritePtr的后面,也就是写指针不能超过读指针。pHeadPtr指向链表中第一个节点的指针。
我使用一个写线程两个读线程,也可以实现两个以上的读线程。朋友们可以试一下只需要修改list中Counters就行了。
DWORD WINAPI ReaderOne(void * pVoid);
DWORD WINAPI ReaderTwo(void * pVoid);
DWORD WINAPI WriterUnique(void * pVoid);
在WriterUnique我采用动态分配节点,同时模拟数据采集填充NODE的buffer数组。当然要对写线程进行加锁保护。在ReaderOne和ReaderTwo中对链表中的节点进行访问并显示在对话框中。
注意的问题:
1, 因为节点是动态分配内存,所以new之后一定要delete否则内存就会被程序慢慢吃掉
2,加锁之后一定要解锁否则就会引起死锁问题。
3,首先点击“启动写线程”然后点击“启动读线程 ”程序在退出之前点击“结束写线程“就可观察到执行效果。