您的位置:首页 > 编程语言

[编程之美] PSet1.10 双线程高效下载

2014-07-27 08:21 239 查看
一,题目

        网络上下载数据,然后存储到硬盘上。简单做法是:先下载一块然后写到硬盘,然后再下载,再写到硬盘上。

        缺点:需要先下载完才能写入硬盘,下载和写是串行操作。

        改进:让两个线程并行进行,设置缓冲区,采用信号量的形式。

                    下载线程,只要缓冲区有空余就下载,下载完成之后告诉写线程缓冲区有数据了。

                     写线程,只要缓冲区有数据就写入,写完后告诉下载线程缓冲区有空闲了。

二,核心源码

假设单线程串行运行的情况下:

//下面是串行实现文件下载与存储
-------API------
//从网络中下载一个数据块
bool GetBlockFromNet(BLOCK *out_Block);
//将数据块信息存入硬盘
bool WriteBlockToDisk(BLOCK *in_Block);
while(true){
bool IsDownloadCompleted;
IsDownloadCompleted = GetBlockFromNet(g_buffer);
WriteBlockToDisk(g_buffer);
if(IsDownloadCompleted)
break;
}


如果希望设计两个线程,使得下载和写硬盘能够并行进行。

线程A:从网络中读取一个数据块,存储到内存的缓冲区中。

线程B:从缓存中读取内容,存储到文件中。

-------API------
class Thread
{
public:
//initalize a thread and set the work function
Thread(void(*work_func)());
//once the object is destructed , the thread will be aborted
~Thread();
//start the thread
void Start();
//stop the thread
void Abort();
};
class Semaphore
{
public:
//initialize semaphore counts
semaphore(int count , int max_count);
~semaphore();
//consume a signal (count--) , block current thread if count==0
void Unsignal();
//raise a signal (count++)
void Signal();
};
//从网络中下载一个数据块
bool GetBlockFromNet(BLOCK *out_Block);
//将数据块信息存入硬盘
bool WriteBlockToDisk(BLOCK *in_Block);
----------------

//分析和解法:
//1.什么时候完成任务?网络数据下载完毕并完全存储到硬盘上,此时正常终止两个线程
//2.下载和存储线程工作条件?
//      所有内容完全下载完毕---下载线程结束;
//      下载工作完成 且 内存缓存区为空---存储线程结束;
//3.共享缓存区数据结构?下载与存储应该满足“先进先出”,保证内容的正确顺序。
#define BUFFER_COUNT 1000
Block g_buffer[BUFFER_COUNT];
Thread g_Download(ProcA);//构造函数初始化两个线程
Thread g_Write(ProcB);

//一开始缓存区空间为BUFFER_COUNT,整个缓存区可供下载数据填充
Semaphore ForDownload(BUFFER_COUNT , BUFFER_COUNT);
//一开始缓存区无数据可供存储
Semaphore ForWrite(0 , BUFFER_COUNT);

//下载任务是否完成
bool g_downloadComplete;
//下载数据从缓存区哪个地方开始填充
int in_index = 0;
//存储数据从缓存区哪个地方开始提取
int out_index = 0;

void main()
{
g_downloadComplete = false;
g_Download.Start();
g_Write.Start();
//wait here till threads finished
}
void ProcA()//下载线程
{
while(true){
ForDownload.Unsignal();
g_downloadComplete = GetBlockFromNet(g_buffer + in_index);
int in_index = (in_index + 1)%BUFFER_COUNT;//循环队列的方式,下标从0开始
ForWrite.Signal();//下载了一个数据块就可以释放一个数据块存储
if(g_downloadComplete)
break;
}
}

void ProcB()//存储线程
{
while(true){
ForWrite.Unsignal();
WriteBlockToDisk(g_buffer + out_index);
int out_index = (out_index + 1)%BUFFER_COUNT;//循环队列方式,下标从0开始
ForDownload.Signal();//存储了一个数据块就可以释放一个数据块覆盖原有数据
if(g_downloadComplete && in_index==out_index)
break;
}
}
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息