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

一,题目

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

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

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

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

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


二,核心源码

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

//下面是串行实现文件下载与存储
-------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;
    }
}


  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值