首页 > 代码库 > 1.10 双线程高效下载

1.10 双线程高效下载

(一)题目

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

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

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

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

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


代码如下:

class Thread {
public:
	Thread(void (*work_func)());
	~Thread();
	void Start();
	void Abort();
};

class Semaphore {
public:
	Semaphore(int count, int max_count);
	~Semaphore();
	void Unsignal();   //count--
	void Signal();     //count++
};

class Mutex {
public:
	WaitMutex();
	ReleaseMutex();
};



//如果使用Mutex,下载和存储线程将不能同时工作,因此,Semaphore是更好的选择
#define BUFFER_COUNT 100
Block g_buffer[BUFFER_COUNT];

Thread g_threadA(ProcA);
Thread g_threadB(ProcB);
Semaphore g_seFull(0, BUFFER_COUNT);    //一开始缓冲区无数据可供存储
Semaphore g_seEmpty(BUFFER_COUNT, BUFFER_COUNT); //一开始缓冲区空间为BUFFER_COUNT,整个缓冲区可供下载的数据填充

bool g_downloadComplete;  //下载任务是否完成
int in_index = 0;    //下载的数据从缓冲区的哪个地方开始填充
int out_index = 0;   //存储的数据从缓冲区的哪个地方开始提取

void main() {
	g_downloadComplete = false;
	g_threadA.Start();
	g_threadB.Start();
}

void ProcA() {
	while(true) {
		g_seEmpty.Unsignal();   //首先取得一个空闲空间,以便下载数据填充
		g_downloadComplete = GetBlockFromNet(g_buffer + in_index);   //填充
		in_index = (in_index + 1) % BUFFER_COUNT;  //更新索引
		g_seFull.Signal();   //提示存储线程可以工作
		if(g_downloadComplete) break;   //当任务全部下载完成,进程就可以结束了
	}
}

void ProcB() {
	while(true) {
		g_seFull.Unsignal();   //查询时候有数据可供存储
		WriteBlockToDisk(g_buffer + out_index);  //存储
		out_index = (out_index + 1) % BUFFER_COUNT;   //更新索引
		g_seEmpty.Signal();   //将空闲空间还给缓冲区
		if(g_downloadComplete && out_index == in_index) break;  //当任务全部下载完成,并且所有的数据都存储到硬盘中,进程才可以结束
	}
}


1.10 双线程高效下载