return0;
}
执行上述代码,这次我们可以清楚地看到在屏幕上交错地输出Fundiplay!
和maindiplay!
,我们发现这两个函数确实是并发运行的,细心的读者可能会发现我们的程序是每当Fun函数和main函数输出内容后就会输出换行,但是我们看到的确是有的时候程序输出换行了,有的时候确没有输出换行,甚至有的时候是输出两个换行。
这是怎么回事?
下面我们把程序改一下看看:
eg3:
#include
#include
uingnamepacetd;
DWORDWINAPIFun(LPVOIDlpParamter)
{
while
(1){cout<<"Fundiplay!
\n";Sleep(1000);}
}
intmain()
{
HANDLEhThread=CreateThread(NULL,0,Fun,NULL,0,NULL);
CloeHandle(hThread);
while
(1){cout<<"maindiplay!
\n";Sleep(2000);}
return0;
}
我们再次运行这个程序,我们发现这时候正如我们预期的,正确地输出了我们想要输出的内容并且格式也是正确的。
下面我就来讲一下此前我们的程序为什么没有正确的运行。
多线程的程序时并发地运行的,多个线程之间如果公用了一些资源的话,我们并不能保证这些资源都能正确地被利用,因为这个时候资源并不是独占的,举个吧:
eg4:
加入有一个资源inta=3
有一个线程函数elfAdd()该函数是使a=a+a
又有一个线程函数elfSub()该函数是使a=a-a
我们假设上面两个线程正在并发欲行,如果elfAdd在执行的时候,我们的目的是想让a编程6,但此时elfSub得到了运行的机会,所以a变成了0,等到elfAdd的到执行的机会后,a=a+a,但是此时a确是0,并没有如我们所预期的那样的到6,我们回到前面eg2,在这里,我们可以把屏幕看成是一个资源,这个资源被两个线程所共用,加入当Fun函数输出了Fundiplay!
后,将要输出endl(也就是清空缓冲区并换行,在这里我们可以不用理解什么事缓冲区),但此时main函数确得到了运行的机会,此时Fun函数还没有来得及输出换行就把CPU让给了main函数,而这时main函数就直接在Fundiplay!
后输出maindiplay!
,至于为什么有的时候程序会连续输出两个换行,读者可以采用同样的分析方法来分析,在这里我就不多讲了,留给读者自己思考了。
那么为什么我们把eg2改成eg3就可以正确的运行呢?
原因在于,多个线程虽然是并发运行的,但是有一些操作是必须一气呵成的,不允许打断的,所以我们看到eg2和eg3的运行结果是不一样的。
那么,是不是eg2的代码我们就不可以让它正确的运行呢?
答案当然是否,下面我就来讲一下怎样才能让eg2的代码可以正确运行。
这涉及到多线程的同步问题。
对于一个资源被多个线程共用会导致程序的混乱,我们的解决方法是只允许一个线程拥有对共享资源的独占,这样就能够解决上面的问题了。
HANDLECreateMute某(
LPSECURITY_ATTRIBUTESlpMute某Attribute,//SD
BOOLbInitialOwner,//initialowner
LPCTSTRlpName//objectname
);
该函数用于创造一个独占资源,第一个参数我们没有使用,可以设为NULL,第二个参数指定该资源初始是否归属创建它的进程,第三个参数指定资源的名称。
HANDLEhMute某=CreateMute某(NULL,TRUE,"creen");
这条语句创造了一个名为creen并且归属于创建它的进程的资源
BOOLReleaeMute某(
HANDLEhMute某//handletomute某
);
该函数用于释放一个独占资源,进程一旦释放该资源,该资源就不再属于它了,如果还要用到,需要重新申请得到该资源。
申请资源的函数如下
DWORDWaitForSingleObject(
HANDLEhHandle,//handletoobject
);
第一个参数指定所申请的资源的句柄,第二个参数一般指定为INFINITE,表示如果没有申请到资源就一直等待该资源,如果指定为0,表示一旦得不到资源就返回,也可以具体地指定等待多久才返回,单位是千分之一秒。
好了,该到我们来解决eg2的问题的时候了,我们可以把eg2做一些修改,如下
eg5:
#include
#include
uingnamepacetd;
HANDLEhMute某;
DWORDWINAPIFun(LPVOIDlpParamter)
{
while
(1){
WaitForSingleObject(hMute某,INFINITE);
cout<<"Fundiplay!
"<Sleep(1000);
ReleaeMute某(hMute某);
}
}
intmain()
{
HANDLEhThread=CreateThread(NULL,0,Fun,NULL,0,NULL);
hMute某=CreateMute某(NULL,FALSE,"creen");
CloeHandle(hThread);
while
(1){
WaitForSingleObject(hMute某,INFINITE);
cout<<"maindiplay!
"<Sleep(2000);
}
return0;
}
运行代码正如我们所预期的输出了我们想要输出的内容。
好了,终于讲完
篇二:
线程函数CreateThread的使用
线程函数CreateThread的使用
HANDLEWINAPICreateThread(
__in_optLPSECURITY_ATTRIBUTESlpThreadAttribute,//指向SECURITY_ATTRIBUTES的指针,为新线程指定安全描述
__inSIZE_TdwStackSize,//初始化线程堆栈尺寸
__inLPTHREAD_START_ROUTINElpStartAddre,//线程函数所指向的地址起始函数__in_optLPVOIDlpParameter,//给线程函数传递的参数
__inDWORDdwCreationFlag,//有关线程的标志
__out_optLPDWORDlpThreadId//系统分配给线程的ID
);
----第一个参数是安全属性,一般设为nil,使用缺省的安全属性。
当我们想此线程有另外的子进程时,可改变它的属性。
----第二个参数是线程堆栈尺寸,一般设为0,表示与此应用的堆栈尺寸相同,即主线程与创建的线程一样长度的堆栈。
并且其长度会根据需要自动变长。
----第四个参数是你需要向线程函数传递的参数,一般是一个指向结构的指针。
不需传递参数时,则这个参数设为nil。
----第五个参数,传入与线程有关的一些标志,如果是CREATE_SUSPENDED,则创建一个挂起的线程,即这个线程本身已创建,它的堆栈也已创建。
但这个线程不会被分配给CPU时间,只有当ReumeThread函数被调用后才能执行;当然,也可以调用SupendThread函数再次挂起线程。
要是标志为0,那么一旦建立线程,线程函数就被立即调用。
一般传为0即可。
----第六个参数是系统分配给这个线程的唯一的ID标志
举例:
DWORDWINAPIThread1Proc(LPVOIDargv)
{
cout<<"Thread1Func\n";
return0;
}
intmain()
{
HANDLEThread;
DWORDThreadID;
Thread1=CreateThread(
0,//默认安全级别
0,//堆栈大小默认(2M)
ThreadProc,//线程入口函数
0,//参数没有
0,//创建时的状态
&Thread1ID//获得线程ID);
cout<<"main\n";
Sleep(100);
return0;
}
篇三:
CreateThread函数解析
随着时代的发展,计算机技术发展得很快。
CPU已经从单核心到多核心的转变,也就是一个CPU里具备了同时做多件事情的能力,而不是过去的分时复用了,而是实实在在地做多件事情。
因此,开发软件也进入了一个新时代,就是多线程软件的开发时代。
如何合理地分配多个线程同时运行,是提高软件效率的关键因素了。
比如像网络游戏的客户端里,就可以使用一个线程不断地更新游戏的界面,分配另外一个线程不断地发送和接收网络的数据,这样CPU的两个核心都在不断地工作。
如果还像以前那样使用一个线程的话,就会发现只有一个内核在做事情。
当然使用线程的编程模型,也会大大地简化软件的复杂性。
下面就来使用线程的API函数。
函数CreateThread声明如下:
WINBASEAPI
__out
HANDLE
WINAPI
CreateThread(
__in_optLPSECURITY_ATTRIBUTESlpThreadAttribute,
__inSIZE_TdwStackSize,
__inLPTHREAD_START_ROUTINElpStartAddre,
__in_optLPVOIDlpParameter,
__inDWORDdwCreationFlag,
__out_optLPDWORDlpThreadId
);
lpThreadAttribute是线程的属性。
dwStackSize是线程的栈大小。
lpStartAddre是线程函数的开始地址。
lpParameter是传送给线程函数的参数。
dwCreationFlag是创建线程标志,比如挂起线程。
lpThreadId是标识这个线程的ID。
调用这个函数的例子如下:
#001//
#002//线程运行函数。
#003//蔡军生2007/09/21
#004//
#005taticDWORDWINAPIThreadProc(
#006LPVOIDlpParameter
#007)
#008{
#009//输出到调试窗口。
#010OutputDebugString(_T("ThreadProc线程函数运行\r\n"));
#011
#012//线程返回码。
#013return0;
#014}
#015
#016
#017
#018//
#019//响应命令.
#021//
#024//菜单选项命令响应:
#025witch(nID)
#026{
#027caeIDC_CREATEBTN:
#028//显示一个按钮。
#029if(!
m_hBtn)
#030{
#031m_hBtn=CreateWindow(_T("BUTTON"),_T("按钮"),#032WS_VISIBLE|WS_CHILD|BS_PUSHBUTTON,#03350,50,100,32,
#034m_hWnd,(HMENU)IDC_BTN,m_hIntance,NULL);#035}
#036break;
#037caeIDC_BTN:
#038{
#039//传给线程的简单参数。
#040intnParam=110;
#041
#042//保存线程的ID。
#043DWORDdwThreadID=0;
#044
#045//创建线程。
#046HANDLEhThread=CreateThread(
#047NULL,//安全属性使用缺省。
#0480,//线程的堆栈大小。
#049ThreadProc,//线程运行函数地址。
#050&nParam,//传给线程函数的参数。
#0510,//创建标志。
#052&dwThreadID);//成功创建后的线程标识码。
#053
#054//等待线程结束。
#055WaitForSingleObject(hThread,INFINITE);
#056
#057//删除的线程资源。
#058CloeHandle(hThread);
#059
#060//
#061OutputDebugString(_T("按钮按下\r\n"));
#062
#063}
#064break;
#065default:
#068