`
mmdev
  • 浏览: 12844260 次
  • 性别: Icon_minigender_1
  • 来自: 大连
文章分类
社区版块
存档分类
最新评论

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

 
阅读更多

一,题目

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

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

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

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

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

二,核心源码


 //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();
 }




分享到:
评论

相关推荐

Global site tag (gtag.js) - Google Analytics