MFC线程(1):简单示例

MFC线程(一):简单示例

线程简介

我们知道一般情况程序中的代码都是按顺序从头开始一行一行的执行以最后.中间不能出现同时执行的情况.比如一段代码调用两个函数

FunOne();

FunTwo();

只要当函数FunOne中的代码执行完才返回来执行FunTwo.假如逻辑上是有先后顺序那还真只能这样按顺序执行下来.不过有假如FunOne与FunTwo没有逻辑先后顺序,是相互独立的.比如两个函数分别处理两不同的文件one.text与two.txt.

这种情形就可以用到线程,弄两个线程去执行这两函数.这样两函数同时执行,提高了效率(如果单核的CPU可能没有真正的并行效果不明显,那多核CPU执行多线程那是能够真正达到并行执行,效果很明显的).

实际上可以这样简单的理解线程,它是CPU的调度单位.而一个线程是对应一个函数.所以别把一个线程想得太复杂,就只是执行个函数而已.只不过执行的时候是并行执行罢了.如果只是简单的几个线程不涉及使用共同的资源,没其他啥关联.就完全跟简单的执行一个函数类似.只是如果多个线程间关系复杂就会涉及到啥同步问题,那样就有很多复杂的细节性问题.

线程分工作线程与界面线程.这里就以工作线程为例

 

最简单示例

1.先来看个MFC中的创建线程的简单例子.

UINT ThreadFun(LPVOID pParam){  //线程要调用的函数

MessageBox(NULL,_T("i am called by a thread."), _T("thread func"),MB_OK);

}

::AfxBeginThread(ThreadFun, NULL);  //这就是创建一个线程并执行了,调用上面的函数弹出一个对话框.

2.示例分析

上面的线程是简单的不能再简单了吧.下面从两个来分析下.

a.首先是被调用的函数有啥讲究不? 当然有,被线程用到的函数格式必须是统一的,返回类型必须是UINT,函数只能有一个参数LPVOID.其中UINT就是个无符号的整形,LPVOID是void*,所以这个参数表示可以传任何类型的指针过来的.

b.函数AfxBeginThread的分析.

这个函数还有返回值CWinThread*的,如果你只是简单的创建一个线程并执行,就不用管了.但如果想要对创建的线程做其他操作就必须这样写.

CWinThread* pThread = ::AfxBeginThread(ThreadFun, NULL); //接下来做啥就直接调用pThead就行.

另外函数AfxBeginThread的参数有很多个,但很多都有默认值.下面是完整的参数

CWinThread* AfxBeginThread(

     AFX_THREADPROC pfnThreadProc,   //一个函数指针
     LPVOID pParam,                               //void*类型的指针,可以传任何种类指针过来.
     int nPriority = THREAD_PRIORITY_NORMAL,     //线程优先级
     UNT nStackSize = 0,                         //分配堆栈大小
     DWORD dwCreateFlags = 0,              //表示线程创建后是立即执行还是等会执行
     LPSECURITY_ATTRIBUTES lpSecurityAttrs = NULL  //线程安全属性指针
     );//用于创建工作者线程

 

上面的参数我们用的最多的是3个,其他一盘都默认值.

AFX_THREADPROC pfnThreadProc //函数指针肯定是必须要指定的,不然线程执行哪个函数去啊

LPVOID pParam //这是传给上面指定函数的参数.如果被调用的函数需要啥参数就只能在这里指定了.

DWORD dwCreateFlags //默认值为0表示创建线程后立即执行.如果是CREATE_SUSPEND则表示创建好后先挂起.必须通过ResumeThread来执行.

 

 

稍复杂点的例子

扩充下上面的例子,给函数传入参数,并且休眠和挂起线程.

UINT ThreadFun(LPVOID pParam){ //线程要调用的函数

int* pNum = (int*)pParam;  //假如会传入一个整形指针参数

MessageBox(NULL,_T("i am called by a thread."), _T("thread func"),MB_OK);

}

CWinThread* pThread; //定义一个线程指针

 

void CreateThread(){//创建一个线程并挂起

int* pNum = new int(88);  //传入的参数

pThread = ::AfxBeginThread(ThreadFun, pNum,THREAD_PRIORITY_NORMAL,0,CREATE_SUSPENDED);

}

 

void StartThread(){ //运行线程

pThread->ResumeThread();

}

 

线程休眠与挂起的区别

上面示例中先创建一个线程,并让它挂起.(suspend),被挂起的线程只有通过ResumeThread才能开始执行.

 

而休眠则一般是这样使用.

UINT ThreadFun(LPVOID pParam){ //线程要调用的函数

::Sleep(1000);  //表示函数执行到这里先休息1000微秒,也就是1秒.然后再接着执行下面的语句.

MessageBox(NULL,_T("i am called by a thread."), _T("thread func"),MB_OK);

}

所以休眠一般是会在线程调用的那个函数中指定.休眠在指定的时间后会自动再次执行,相当于暂停一断时间然后又自动活过来了,不用像挂起还必须得显式去启动才行.