/*
双线程高效下载:
下载一块数据,写入硬盘,然后再下载,再写入硬盘,不断重复这个过程,直到所有的内容下载完毕为止。能否
对此进行优化?
1假设所有数据块的大小都是固定的。你可以使用一个全局缓存区:
Block g_buffer[BUFFER_COUNT]
2假设两个基本函数已经实现(你可以假定两个函数都能正常工作,不会抛出异常)
bool GetBlockFromNet(Block* out_block);//从网上下载sequentially:从而,在每个调用中都返回true,如果
完整的文件被下载下来,否则为false
bool WriteBlockToDisk(Block* in_block)//写一块数据到硬盘中
为了提高效率,希望设计两个线程,使得下载和写硬盘能够并行执行
线程A:从网络中读取一个数据块,存储到内存的缓存中
线程B:从缓存中读取内容,存储到文件中
请实现如下子程序:
1初始化部分
2线程A
3线程B
如果网络延迟为L1,磁盘I/O延迟为L2,将此多线程与单线程进行比较,分析这个设计的性能问题,并考虑是否还有
其他改进的设计方法?
当时做项目的时候:我是写入到vector中,一旦到时间,就对vector加锁,然后取出数据处理,处理完后清空
然后释放锁,因此起始从取数据到处理数据的确是串行的
分析:
1什么时候算是完成任务?
两个线程必须协同工作,将网络上的数据下载完并存储到硬盘上,线程才能终止
2希望两个线程能尽可能同时工作
如果使用Mutex,下载和存储线程将不能同时工作。因此,Semaphore是更好的选择。
3下载和存储线程工作的必要条件:
如果共享缓存区已经满了,应该暂停下载。如果所有内容全部下载完毕,没必要再下载。
如果缓存区为空,没必要云心存储吸纳成。如果下载结束,存储线程也结束。
4共享缓存区的数据结构
先进先出应该用队列。因为采用了固定的缓冲空间保存下载内容,因此用循环队列。
*/
/*
关键:
1 生产者消费者问题,需要设置信号量来实现。如果一个程序由k个串行部分组成,则对每个串行部分用一个线程
即可
2 共享缓存区的数据结构
先进先出应该用队列。因为采用了固定的缓冲空间保存下载内容,因此用循环队列。
3 Thread(void (*work_func)());//实例化一个线程,并设置工作函数
Thread g_threadA(procA);//注意线程里面是用函数指针来构造的
4 void Unsignal()//消费一个信号,则信号数减1,如果计数器变为0,则阻塞当前线程
{
_iCount--;
}
5 Semaphore g_semFull(0,BLOCK_COUNT);//起初设置空缓冲数=缓冲区大小
6 g_semFull.Unsignal();//信号量操作,而不使用加锁操作,能够实现并行程序
7 WriteBlockToDisk(g_buffer + g_outIndex);//如果网络延迟为L1,磁盘I/O延迟为L2,这个多线程执行
//时间约为Max(L1,L2)参见操作系统之读写时间。单线程时间为L1+L2。如果网络延迟远大于I/O延迟
//则多个下载线程的设计可进一步改善性能。
8 g_outIndex = (g_outIndex + 1) % BLOCK_COUNT;//形成循环队列
9 if(g_downloadComplete && g_inIndex == g_outIndex)
//如果已经下载完毕并且缓冲区内容为空(消费者追上生产者,参见循环队列
//:判空:队头=队尾,判满:队尾+1 = 队头)
{
break;
}
*/
#include <stdio.h>
const int BLOCK_COUNT = 1000;
typedef struct Block
{
}Block;
class Thread
{
public:
Thread(void (*work_func)());//实例化一个线程,并设置工作函数
~Thread();
void start();
void abort();
};
class Semaphore
{
public:
Semaphore(int count,int max_count):_iCount(count),_iMaxCount(max_count){}//初始化信号计数器
~Semaphore(){}
void Unsignal()//消费一个信号,则信号数减1,如果计数器变为0,则阻塞当前线程
{
_iCount--;
}
void Signal()//增加一个信号,信号数加1
{
_iCount++;
}
private:
int _iCount;
int _iMaxCount;
};
class Mutex
{
public:
void WaitMutex();//阻塞线程,知道释放这个锁
void ReleaseMutex();//释放锁,让其他线程可以等待进入
};
bool GetBlockFromNet(Block* out_block);
bool WriteBlockToDisk(Block* in_block);
void procA();
void procB();
Thread g_threadA(procA);//注意线程里面是用函数指针来构造的
Thread g_threadB(procB);
Semaphore g_semFull(0,BLOCK_COUNT);//起初设置空缓冲数=缓冲区大小
Semaphore g_semEmpty(BLOCK_COUNT,BLOCK_COUNT);
bool g_downloadComplete;
int g_inIndex = 0;
int g_outIndex = 0;
Block g_buffer[BLOCK_COUNT];
void procA()//生产者
{
while(true)
{
g_semEmpty.Unsignal();
g_downloadComplete = GetBlockFromNet(g_buffer + g_inIndex);
g_inIndex = (g_inIndex + 1) % BLOCK_COUNT;//形成循环队列
g_semFull.Signal();
if(g_downloadComplete)
{
break;
}
}
}
void procB()//消费者进程
{
while(true)
{
g_semFull.Unsignal();//信号量操作,而不使用加锁操作,能够实现并行程序
WriteBlockToDisk(g_buffer + g_outIndex);//如果网络延迟为L1,磁盘I/O延迟为L2,这个多线程执行
//时间约为Max(L1,L2)参见操作系统之读写时间。单线程时间为L1+L2。如果网络延迟远大于I/O延迟
//则多个下载线程的设计可进一步改善性能。
g_outIndex = (g_outIndex + 1) % BLOCK_COUNT;//形成循环队列
g_semEmpty.Signal();
if(g_downloadComplete && g_inIndex == g_outIndex)
//如果已经下载完毕并且缓冲区内容为空(消费者追上生产者,参见循环队列
//:判空:队头=队尾,判满:队尾+1 = 队头)
{
break;
}
}
}
bool GetBlockFromNet(Block* out_block)
{
return true;
}
bool WriteBlockToDisk(Block* in_block)
{
return true;
}
void download()
{
while(true)
{
bool isComplete;
isComplete = GetBlockFromNet(g_buffer);
WriteBlockToDisk(g_buffer);//然后向硬盘中写数据
if(isComplete)
{
break;
}
}
}
void process()
{
g_downloadComplete = false;
g_threadA.start();
g_threadB.start();
//等待直到线程结束
}
int main(int argc,char* argv[])
{
process();
getchar();
return 0;
}