WinInet多线程下载器编写历程(1)

昨天第一天开始编写使用Win32 API : WinInet 的多线程下载器,作为毕业设计,我准备把开发的过程记录下来。

总结一下昨天遇到的问题吧:


(1)架构

我初步架构了一下:

1.每个下载任务,即一个CDingDownload的对象,对象成员包括此下载任务的各种配置信息:

源URL,目标文件名,线程数,块大小,Owner窗口句柄(发通知消息用)

2.CDingDownload中有一个主控线程,控制多线程下载

问题出现了: 线程的过程函数必须是类中的static函数,但是static是没有this指针的,只能访问静态变量

为了解决这个问题,我将this指针放在主控线程 过程函数的参数结构体里面,这样static线程也能区分不同的对象了,可以通过指针访问对象成员

struct MainControlParam
{
	CDingHttpDownload * p_this;
};

VOID CDingHttpDownload::Start()
{
m_state = running;
m_mainctlparam.p_this = this;
m_thread_maincontrol = CreateThread(NULL,0,MainControlProc,&m_mainctlparam,0,NULL);
}

DWORD WINAPI CDingHttpDownload::MainControlProc(LPVOID lpParam)
{
	MainControlParam *pParam = (MainControlParam *)lpParam;
	CDingHttpDownload *pthis = pParam->p_this;				//对象指针
	HWND hwnd = pthis->m_hwnd;								//主窗口句柄
	PTCHAR URL = pthis->m_URL;
}


3.主控线程获取文件信息,开始分发任务下载(创建下载线程),并发送自定义消息通知窗口


关于自定义消息的使用:

首先定义消息,自定义消息都是从WM_USER往上的

#define WM_USER_THREAD_REQUEST (WM_USER + 0x101)


然后声明定义回调函数

afx_msg LRESULT OnThreadRequest(WPARAM wparam,LPARAM lparam);

LRESULT CDownloaderDlg::OnThreadRequest(WPARAM wparam,LPARAM lparam)
{
return 0;
}

最后加入消息映射

BEGIN_MESSAGE_MAP(CDownloaderDlg, CDialogEx)
	ON_WM_SYSCOMMAND()
	ON_WM_PAINT()
	ON_WM_QUERYDRAGICON()
	ON_BN_CLICKED(IDC_BUTTON1, &CDownloaderDlg::OnBnClickedButton1)
	ON_MESSAGE(WM_USER_THREAD_REQUEST,OnThreadRequest)
END_MESSAGE_MAP()



4.主控线程维护一个信号量来控制线程数,开始不断创建任务


semaphore_threads = CreateSemaphore(NULL,pthis->m_threadnum,pthis->m_threadnum,NULL);


while循环,获取信号量后创建下载线程

	while (pthis->m_state == running)
	{
		WaitForSingleObject(semaphore_threads,INFINITE);

		DownloadThreadParam *param = new DownloadThreadParam();
		param->p_this = pthis;
		param->mutex_progress = mutex_progress;
		param->semaphore_threads = semaphore_threads;
		param->range1 = 0;
		param->range2 = 1024;
		CreateThread(NULL,0,DownloadProc,param,0,NULL);
	}

下载线程下载完成自己的任务块之后释放信号量:

DownloadProc尾部:

ReleaseSemaphore(pParm->semaphore_threads,1,NULL);


(2)遇到的问题

1.内存分配的问题:

DownloaderDlg.cpp中的按钮响应函数中构造一个下载类:

注意我注释掉的部分使用new来申请一个buffer然后传递给下载类的构造函数

	//PTCHAR URL = new TCHAR(50);
	PTCHAR URL = (PTCHAR)GlobalAlloc(0,50*sizeof(TCHAR));
	_tcscpy(URL,_T("127.0.0.1"));
	//PTCHAR filename = new TCHAR(50);
	PTCHAR filename = (PTCHAR)GlobalAlloc(0,50*sizeof(TCHAR));
	_tcscpy(filename,_T("ding.down.php"));

	m_pdownload = new CDingHttpDownload(this->m_hWnd,URL,filename);


httpcore.cpp构造函数部分:

CDingHttpDownload::CDingHttpDownload(HWND handle,PTCHAR URL,PTCHAR filename,\
        UINT threadnum,UINT blocksize,UINT cache)\
	:m_hwnd(handle),m_threadnum(threadnum),m_blocksize(blocksize),m_cache(cache)
{

	//m_URL = new TCHAR(50);
	m_URL = (PTCHAR)GlobalAlloc(0,(_tcslen(URL)+1)*sizeof(TCHAR));
	_tcscpy(m_URL,URL);
	//m_filename = new TCHAR(50);
	m_filename = (PTCHAR)GlobalAlloc(0,(_tcslen(filename)+1)*sizeof(TCHAR));
	_tcscpy(m_filename,filename);
}

如果我使用注释掉的new方式来申请内存,会在运行过程中遇到错误,并提示"其原因可能是堆被损坏,这也说明程序中或它所加载的任何DLL 中有bug"

如果改成GlobalAlloc就没问题了,可是我的程序并没有使用DLL啊,应该还是一个堆呀?

难道是MFC和这段代码用的是不同的堆吗?

注:我使用的是静态链接MFC库的方式。

刚才调试了一下,发现在httpcore.cpp,构造函数里面new的地址和在DownloaderDlg.cpp中new地址相差很大。。


你可能感兴趣的:(thread,多线程,Semaphore,null,url,任务)