【编程之美】双线程高效下载

来源:互联网 发布:ubuntu 清理 tmp 编辑:程序博客网 时间:2024/04/29 01:13

一,题目

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

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

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

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

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

        

二,核心源码


 //downloads a block from Internet sequentially in each call //return true, if the entire file is downloaded, otherwise false. bool GetBlockFromNet(Block* out_block);  //writes a block to hard disk bool WriteBlockToDisk(Block* in_block);  class Thread { public:     Thread(void (*work_func)());     ~Thread();     void Start();     void Abort(); };  class Semaphore { public:     Semaphore(int count,int max_count);     ~Semaphore();     void Unsignal();     void Signal(); };  class Mutex { public:     WaitMutex();     ReleaseMutex(); }; //----------------------------------------------------    //1.确定使用信号量,而非互斥量,保证并行操作 //2.当缓冲区并不满并且下载没结束时,下载线程运行 //3.当缓冲区并不空并且下载没结束时,存储线程运行   #define MAX_COUNT 1000 Block g_Buffer[MAX_COUNT]; //缓冲区数组,模拟循环队列 Thread g_Download(ProcA); Thread g_Write(ProcB);  //一开始缓冲区空间为MAX_COUNT,整个缓冲区可供下载的数据填充 Semaphore ForDownload(MAX_COUNT,MAX_COUNT); //一开始缓冲区无数据可供存储 Semaphore ForWrite(0,MAX_COUNT);  //下载任务是否完成 bool isDone; //下载的数据从缓冲区的哪个地方开始填充 int in_index; //存储的数据从缓冲区的哪个地方开始提取 int out_index;  void ProcA()//下载线程  {     while(true)     {         //首先取得一个空闲空间,以便下载数据填充         ForDownload.Unsignal();         //填充         isDone=GetBlockFromNet(g_Buffer+in_index);         //更新索引         in_index=(in_index+1)%MAX_COUNT;         //提示存储线程可以工作         ForWrite.Signal();              //当任务全部下载完成,进程就可以结束了         if(isDone)               break;     } }  void ProcB()//写入线程  {     while(true)     {         //查询时候有数据可供存储         ForWrite.Unsignal();         //存储         WriteBlockToDisk(g_Buffer+out_index);         //更新索引         out_index=(out_index+1)%MAX_COUNT;         //将空闲空间还给缓冲区         ForDownload.Signal();              //当任务全部下载完成,并且所有的数据都存储到硬盘中,进程才可以结束         if(isDone&&in_index==out_index)             break;     } }  int main() {     isDone=false;     in_index=0;     out_index=0;     g_Download.Start();     g_Write.Start(); }