多线程编程的一点小心得(1) 推荐
2012-05-08 17:45
429 查看
最近有了很多想法,想把我用过的东西都吃透,这样才不会变成所谓的“样样通样样松”。我是新手,老鸟请飘过,当然,这篇小心得如果有什么毛病,还请指出来。先行谢过!
其实我本来想把博客当作自己的日记,记录下学习的点点滴滴,写下的就代表是学会的东西,人家说好记性不如烂笔头嘛。
一直以来对多线程这块就迷迷糊糊的,用得不太多,即便是用了,也是把以前写的代码拿出来,稍微修改一下,就适应了新的需求。也看过一些资料,但都没实践过,所以就马马虎虎地,能够适应工作需求就得过且过。其实这种思想是非常错误的。做技术一定要踏实,否则就无法成长。
闲话少说书归正传,接下来的几篇就把多线程的东西学习、总结一下。先上一段万金油:
线程:有时被称为轻量级进程(Lightweight Process,LWP),是程序执行流的最小单元。一个标准的线程由线程ID,当前指令指针(PC),寄存器集合和堆栈组成。另外,线程是进程中的一个实体,是被系统独立调度和分派的基本单位,线程自己不拥有系统资源,只拥有一点在运行中必不可少的资源,但它可与同属一个进程的其它线程共享进程所拥有的全部资源。一个线程可以创建和撤消另一个线程,同一进程中的多个线程之间可以并发执行。由于线程之间的相互制约,致使线程在运行中呈现出间断性。线程也有就绪、阻塞和运行三种基本状态。每一个程序都至少有一个线程,那就是程序本身。
自从有了线程,这个世界就变得吵起来了,线程的周期、调度与优先级、资源共享、线程同步、守护线程、死锁、信号量。。后面咱们再慢慢研究这些东西吧,今天先来简单明白线程到底是什么,怎么应用在编程中。
老惯例,上个程序吧。建立一个标准的基于对话框的MFC程序,拖一个edit控件和一个button在上面,资源命名分别为ID_EDIT_NUMBER和ID_BUTTON_START。给edit控件关联一个变量CEdit * m_editNumber。弄差不多这个样子就行。
先写一个线程函数,告诉电脑在这个线程里要做什么。
很简单,就是让edit控件显示不停增加的数字。
接下来就要想办法启动这个线程。
在CMultithreadTestDlg中添加一个成员变量HANDLE m_hThread。双击start按钮,编写按钮的单击事件。
CreateThread函数的原型如下:
第一个参数是安全属性,指向一个LPSECURITY_ATTRIBUTES类型的结构体,一般设为NULL;
第二个参数是线程的堆栈大小,如果不是内存特别紧张的话,就设为0,表示windows将动态调整堆栈的大小;
第三个参数是指向线程函数的指针,其实就是函数名。函数名随便起,但是在声名函数时必须要遵守形式
否则就无法成功调用;
第四个参数是向线程函数传递的参数,不传递时就设为NULL;
第五个参数是线程标志,有两个可取值:
1). CREATE_SUSPENDED,表示创建后立即挂起
2). 0,表示正常创建,创建后立刻运行
第六个参数用来保存新建线程的ID,如果不需要处理线程ID的话,则可传入NULL。
返回值是线程的句柄。
运行时效果如下
这个程序其实是有风险的,风险有二:
1). 在MFC程序中,应该尽量使用AfxBeginThread方法来创建线程。
2). 如果我不停地按start,一会内存就用光了=。=
2的解决方法就不上了,无非是使用标志位,线程没跑完之前不再创建新的线程。
来说说AfxBeginThread。这是MFC中的比较安全的线程创建方法。函数原型如下:
有两个可以重载的函数,常用的是第一个。也能看出来,第一个函数与CreateThread()的参数其实是差不多的,只不过顺序不太一样。需要注意的是第二个重载函数,参数一是CRuntimeClass * pThreadClass,CRuntimeClass是个结构体,MSDN里的解释是“The RUNTIME_CLASS of an object derived from CWinThread.”为此我特意看了一下AfxBeginThread的源代码,其中有如下一行:
表明RUNTIME_CLASS是个宏定义。
也就是用这个宏将线程类指针转换为指向CRuntimeClass的对象指针。
那么新的线程创建语句就变为了:
而且需要将线程函数的声明修改一下:
线程执行的中间是可以暂停的,使用DWORD CWinThread::SuspendThread()函数即可。暂停后可以使用DWORD CWinThread::ResumeThread()函数使线程恢复运行。
这是基本用法,至于一些高级点儿的东西,明儿继续。
PS:正所谓懂得越多就发现懂得越少,今天搜资料,又搜出好多没听过的东西=。=,全部记在本子上,逐个消灭之。。
PSS:下一个目标,看明白与这个网页相关联的东西。。http://en.wikipedia.org/wiki/Thread_(computing)
PSSS:这玩意儿真形象。。
其实我本来想把博客当作自己的日记,记录下学习的点点滴滴,写下的就代表是学会的东西,人家说好记性不如烂笔头嘛。
一直以来对多线程这块就迷迷糊糊的,用得不太多,即便是用了,也是把以前写的代码拿出来,稍微修改一下,就适应了新的需求。也看过一些资料,但都没实践过,所以就马马虎虎地,能够适应工作需求就得过且过。其实这种思想是非常错误的。做技术一定要踏实,否则就无法成长。
闲话少说书归正传,接下来的几篇就把多线程的东西学习、总结一下。先上一段万金油:
线程:有时被称为轻量级进程(Lightweight Process,LWP),是程序执行流的最小单元。一个标准的线程由线程ID,当前指令指针(PC),寄存器集合和堆栈组成。另外,线程是进程中的一个实体,是被系统独立调度和分派的基本单位,线程自己不拥有系统资源,只拥有一点在运行中必不可少的资源,但它可与同属一个进程的其它线程共享进程所拥有的全部资源。一个线程可以创建和撤消另一个线程,同一进程中的多个线程之间可以并发执行。由于线程之间的相互制约,致使线程在运行中呈现出间断性。线程也有就绪、阻塞和运行三种基本状态。每一个程序都至少有一个线程,那就是程序本身。
自从有了线程,这个世界就变得吵起来了,线程的周期、调度与优先级、资源共享、线程同步、守护线程、死锁、信号量。。后面咱们再慢慢研究这些东西吧,今天先来简单明白线程到底是什么,怎么应用在编程中。
老惯例,上个程序吧。建立一个标准的基于对话框的MFC程序,拖一个edit控件和一个button在上面,资源命名分别为ID_EDIT_NUMBER和ID_BUTTON_START。给edit控件关联一个变量CEdit * m_editNumber。弄差不多这个样子就行。
先写一个线程函数,告诉电脑在这个线程里要做什么。
DWORD _stdcall ThreadProc(LPVOID lpParameter) { CMultithreadTestDlg * dlg = (CMultithreadTestDlg*) lpParameter; CString szCounter; for(int i = 0; i < 10000; i++) { szCounter.Format(_T("%d"), i); dlg->m_editNumber.SetWindowTextW(szCounter); szCounter.ReleaseBuffer(); } return 0; }
很简单,就是让edit控件显示不停增加的数字。
接下来就要想办法启动这个线程。
在CMultithreadTestDlg中添加一个成员变量HANDLE m_hThread。双击start按钮,编写按钮的单击事件。
void CMultithreadTestDlg::OnBnClickedButtonStart() { // TODO: Add your control notification handler code here m_hThread = CreateThread(NULL, 0, ThreadProc, this, 0, NULL); }
CreateThread函数的原型如下:
HANDLE WINAPI CreateThread( __in_opt LPSECURITY_ATTRIBUTES lpThreadAttributes, __in SIZE_T dwStackSize, __in LPTHREAD_START_ROUTINE lpStartAddress, __in_opt LPVOID lpParameter, __in DWORD dwCreationFlags, __out_opt LPDWORD lpThreadId );
第一个参数是安全属性,指向一个LPSECURITY_ATTRIBUTES类型的结构体,一般设为NULL;
第二个参数是线程的堆栈大小,如果不是内存特别紧张的话,就设为0,表示windows将动态调整堆栈的大小;
第三个参数是指向线程函数的指针,其实就是函数名。函数名随便起,但是在声名函数时必须要遵守形式
DWORD WINAPI ThreadProc(LPVOID lpParameter)
否则就无法成功调用;
第四个参数是向线程函数传递的参数,不传递时就设为NULL;
第五个参数是线程标志,有两个可取值:
1). CREATE_SUSPENDED,表示创建后立即挂起
2). 0,表示正常创建,创建后立刻运行
第六个参数用来保存新建线程的ID,如果不需要处理线程ID的话,则可传入NULL。
返回值是线程的句柄。
运行时效果如下
这个程序其实是有风险的,风险有二:
1). 在MFC程序中,应该尽量使用AfxBeginThread方法来创建线程。
2). 如果我不停地按start,一会内存就用光了=。=
2的解决方法就不上了,无非是使用标志位,线程没跑完之前不再创建新的线程。
来说说AfxBeginThread。这是MFC中的比较安全的线程创建方法。函数原型如下:
CWinThread* AfxBeginThread( AFX_THREADPROC pfnThreadProc, LPVOID pParam, int nPriority = THREAD_PRIORITY_NORMAL, UINT nStackSize = 0, DWORD dwCreateFlags = 0, LPSECURITY_ATTRIBUTES lpSecurityAttrs = NULL ); CWinThread* AfxBeginThread( CRuntimeClass* pThreadClass, int nPriority = THREAD_PRIORITY_NORMAL, UINT nStackSize = 0, DWORD dwCreateFlags = 0, LPSECURITY_ATTRIBUTES lpSecurityAttrs = NULL );
有两个可以重载的函数,常用的是第一个。也能看出来,第一个函数与CreateThread()的参数其实是差不多的,只不过顺序不太一样。需要注意的是第二个重载函数,参数一是CRuntimeClass * pThreadClass,CRuntimeClass是个结构体,MSDN里的解释是“The RUNTIME_CLASS of an object derived from CWinThread.”为此我特意看了一下AfxBeginThread的源代码,其中有如下一行:
ASSERT(pThreadClass->IsDerivedFrom(RUNTIME_CLASS(CWinThread)));
表明RUNTIME_CLASS是个宏定义。
#define RUNTIME_CLASS(class_name) (class_name::GetThisClass())
也就是用这个宏将线程类指针转换为指向CRuntimeClass的对象指针。
那么新的线程创建语句就变为了:
CWinThread * m_thread; // m_thread为成员变量 m_thread = AfxBeginThread(ThreadProc, this);
而且需要将线程函数的声明修改一下:
UINT ThreadProc(LPVOID lpParameter)
线程执行的中间是可以暂停的,使用DWORD CWinThread::SuspendThread()函数即可。暂停后可以使用DWORD CWinThread::ResumeThread()函数使线程恢复运行。
这是基本用法,至于一些高级点儿的东西,明儿继续。
PS:正所谓懂得越多就发现懂得越少,今天搜资料,又搜出好多没听过的东西=。=,全部记在本子上,逐个消灭之。。
PSS:下一个目标,看明白与这个网页相关联的东西。。http://en.wikipedia.org/wiki/Thread_(computing)
PSSS:这玩意儿真形象。。
相关文章推荐
- 多线程编程的一点小心得(2)
- 关于C#多线程、网络编程与计时器Timer的一点使用心得
- [Unity]多线程编程的一点心得
- [原创]linux 多线程 socket编程一些心得_凌晓_百度空间
- 编程开发的一点学习心得
- 多线程应用程序中调用窗体的一点心得
- CEGUI界面编程的一点心得
- 多线程编程博客推荐及phread线程的深入理解
- 关于多线程编程的一点思考
- 与大家分享一点有关编程的心得
- JMS(Jboss Messaging)的一点使用心得(十二)多线程的ClassLoader
- Project Euler,值得推荐的编程网站以及我的一点体会
- Java多线程和并发编程实践的学习心得----基础篇
- Linux 多线程 网络编程 管道 socket等相关心得
- 关于exe形式编程的一点心得,希望对大家有所帮助
- Java多线程编程相关资料推荐
- 关于exe形式编程的一点心得,希望对大家有所帮助
- 多线程应用程序中调用窗体的一点心得
- IntelliJ IDEA一点心得,新手推荐(转)
- 多线程编程的一点点心得