编程之美——双线程高效下载

编程之美——双线程高效下载

一,题目

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

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

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

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

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

二,核心源码

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

    原文作者:快乐的霖霖
    原文地址: https://blog.csdn.net/chdhust/article/details/8348402
    本文转自网络文章,转载此文章仅为分享知识,如有侵权,请联系博主进行删除。
点赞