2024年10月createthread实现原理(VB中如何利用Createthread实现多线程能给个最简单的例子吗)

 更新时间:2024-10-12

  ⑴createthread实现原理(VB中如何利用Createthread实现多线程能给个最简单的例子吗

  ⑵VB中如何利用Createthread实现多线程能给个最简单的例子吗

  ⑶OptionExplicitPublicDeclareFunctionCreateThreadLib“kernel“(ByVallpThreadAttributesAsAny,ByValdwStackSizeAsLong,ByVallpStartAddressAsLong,lpParameterAsAny,ByValdwCreationFlagsAsLong,lpThreadIDAsLong)AsLongPublicDeclareFunctionTerminateThreadLib“kernel“(ByValhThreadAsLong,ByValdwExitCodeAsLong)AsLongPublicidAsLongPublicFunctionAddText()DoForm.Text.Text=“AddingtoText-“doeventsLoopEndFunctionOptionExplicitPrivateSubmand_Click()id=CreateThread(ByVal&,ByVal&,AddressOfAddText,ByVal&,,id)EndSubPrivateSubmand_Click()CallTerminateThread(id,ByVal&)EndSub//PS请注意里面所用到的控件...

  ⑷C语言中如何实现两个while(同时进行求指点

  ⑸恩,有想法肯定是好的。两个while循环不可能同时运行。及时在java,c#里也不能。CPU一次只能执行一个指令,不能同时执行两个。由于CPU在不同线程里切换的速度很快。所以可以实现你所想要的要求。

  ⑹C语言中怎么实现双线程或者父子线程啊

  ⑺通常使用CreateThread函数来创建新的线程.(Unix下使用pthread_create函数)首先指出,线程与线程之间,是并列关系,不会存在“父子线程“的概念.在Windows平台下,CreateThread函数包含在Windows.h文件内,包含此文件即可正常使用.以下为CreateThread函数的声明:HANDLECreateThread(LPSECURITY_ATTRIBUTESlpThreadAttributes,//指向安全性属性描述结构体的//指针,通常可以忽略的.SIZE_TdwStackSize,//指定新线程初始的栈大小,若不关心,可以用填充,来要求使用//默认值LPTHREAD_START_ROUTINElpStartAddress,//用来充当线程的函数的指针.LPVOIDlpParameter,//要传递给函数的参数,这个值本身就是那个参数,而不是参数的地址DWORDdwCreationFlags,//创建的方式,表示正常,创建后立即开始运行LPDWORDlpThreadId//用来接受函数反馈的线程ID的指针.);用来充当新的线程的函数格式:DWORDWINAPIThreadProc(LPVOID);CreateThread函数若成功了,返回新线程的句柄,若失败了,则返回NULL.若用CREATE_SUSPENDED填充dwCreationFlags则创建的线程先挂起来,并不直接开始运行,要用ResumeThread函数恢复线程,才能继续运行.

  ⑻如何避免使用CreateThread函数导致的内存泄露

  ⑼CreateThread导致内存泄露的原因这得从C运行时库说起了。VC运行时库,有一个宏errno,用来获得上一步操作的错误码,类似于Win中的GetLastError()函数。在多线程环境下,不同线程调用errno返回的都是caller线程的错误码,绝对不会混淆,这是因为使用了TLS技术。TLS,ThreadLocalStorage,是用来存取线程相关数据的一种技术,在Win中由操作系统的Tls*系列函数提供支持。例如,可以在程序开始的地方调用TlsAlloc()函数,获得一个TLSindex,这个index在进程范围内有效,然后可以创建n个线程,在每个线程中使用TlsSetValue(index,data)将线程相关数据和index关联起来,使用TlsGetValue(index)来获取当前线程和index相关联的的线程相关数据。查看msdn可以发现,Tls*函数的定义如下:viewplaincopyDWORDWINAPITlsAlloc(void);BOOLWINAPITlsSetValue(__inDWORDdwTlsIndex,__inLPVOIDlpTlsValue);LPVOIDWINAPITlsGetValue(__inDWORDdwTlsIndex);BOOLWINAPITlsFree(__inDWORDdwTlsIndex);观察TlsSetValue/TlsGetValue的原型可以发现,与index关联的数据只能是void*类型,因此通常的做法是在线程开始的时候,为这个线程分配一块内存,用于存储所有与线程相关的数据,然后把这块内存的起始地址与index关联起来。如果这块内存在线程退出的时候没有释放掉,那就有内存泄露的危险。回到errno,来看看C运行时库是如何实现errno的。errno的声明和实现如下:viewplaincopy/*error.h-errno的声明*/_CRTIMPexternint*__cdecl_errno(void);#defineerrno(*_errno())/*dosmap.c-errno的实现*/int*__cdecl_errno(void){_ptiddataptd=_getptd_noexit();if(!ptd){return&ErrnoNoMem}else{return(&ptd-》_terrno);}}观察_errno的代码,函数首先调用了_getptd_noexit()函数,这个函数的代码如下:viewplaincopy/*tiddata.c-_getptd_noexit()实现*/_ptiddata__cdecl_getptd_noexit(void){_ptiddataptd;DWORDTL_LastError;TL_LastError=GetLastError();#ifdef_M_IX/**InitializeFlsGetValuefunctionpointerinTLSbycalling__set_flsgetvalue()*/if((ptd=(__set_flsgetvalue())(__flsindex))==NULL){#else/*_M_IX*/if((ptd=FLS_GETVALUE(__flsindex))==NULL){#endif/*_M_IX*//**noper-threaddatastructureforthisthread.trytocreate*one.*/#ifdef_DEBUGexternvoid*__cdecl_calloc_dbg_impl(size_t,size_t,int,constchar*,int,int*);if((ptd=_calloc_dbg_impl(,sizeof(struct_tiddata),_CRT_BLOCK,__FILE__,__LINE__,NULL))!=NULL){#else/*_DEBUG*/if((ptd=_calloc_crt(,sizeof(struct_tiddata)))!=NULL){#endif/*_DEBUG*/if(FLS_SETVALUE(__flsindex,(LPVOID)ptd)){/**Initializeofper-threaddata*/_initptd(ptd,NULL);ptd-》_tid=GetCurrentThreadId();ptd-》_thandle=(uintptr_t)(-);}else{/**ReturnNULLtoindicatefailure*/_free_crt(ptd);ptd=NULL;}}}SetLastError(TL_LastError);return(ptd);}_getptd_noexit()函数首先通过TLS查找线程相关数据,如果没有找到,就分配一块内存,存放_tiddata结构,并将这块内存与__flsindex相关联。由此可见,errno的确使用了TLS技术,而且通过查找_getptd_noexit()可以发现,VC运行时库中很多很多函数都使用了TLS,errno只不过是其中的一个典型。

  ⑽用c++怎么写线程池求解

  ⑾我在原来在网上找的资源,你可以参考一下。线程池线程是一种比较昂贵的资源.有些系统为了重用线程.引入了线程池的机制.线程池的工作原理如下:首先.系统会启动一定数量的线程.这些线程就构成了一个线程池.当有任务要做的时候.系统就从线程池里面选一个空闲的线程.然后把这个线程标记为“正在运行”.然后把任务传给这个线程执行.线程执行任务完成之后.就把自己标记为空闲.这个过程并不难以理解.难以理解的是.一般来说.线程执行完成之后.运行栈等系统资源就会释放.线程对象就被回收了.一个已经完成的线程.又如何能回到线程池的空闲线程队列中呢秘诀就在于.线程池里面的线程永远不会执行完成.线程池里面的线程都是一个无穷循环ThreadStarter.h#ifndef__THREADSTARTER_H__#define__THREADSTARTER_H__#includewindows.h线程接口classThreadBase{publicThreadBase(){}virtual~ThreadBase(){}virtualboolrun()=;线程函数virtualvoidOnShutdown(){}HANDLETHREAD_HANDLE;};#endifThreads.h#ifndef__CTHREADS_H__#define__CTHREADS_H__#includeThreadStarter.h线程的状态enumCThreadState{THREADSTATE_TERMINATE=,终止THREADSTATE_PAUSED=,暂停THREADSTATE_SLEEPING=,睡眠THREADSTATE_BUSY=,忙碌THREADSTATE_AWAITING=,等候};线程基类classCThreadpublicThreadBase{publiThread();~CThread();virtualboolrun();virtualvoidOnShutdown();设置线程的状态__forceinlinevoidSetThreadState(CThreadStatethread_state){ThreadState=thread_state;}返回线程的状态__forceinlineCThreadStateGetThreadState(){returnThreadState;}返回线程IDintGetThreadId(){returnThreadId;}time_tGetStartTime(){returnstart_time;}protectedCThreadStateThreadState;线程的状态time_tstart_time;intThreadId;线程ID};#endifThreads.cpp#includestdafx.h#includeCThreads.hCThreadCThread()ThreadBase(){初试化线程的状态为等候ThreadState=THREADSTATE_AWAITING;start_time=;}CThread~CThread(){}boolCThreadrun(){returnfalse;}voidCThreadOnShutdown(){SetThreadState(THREADSTATE_TERMINATE);}Mutex.h#ifndef__MUTEX_H__#define__MUTEX_H__#includewindows.h多个线程操作相同的数据时,一般是需要按顺序访问的,否则会引导数据错乱为解决这个问题,就需要引入互斥变量,让每个线程都按顺序地访问变量。classMutex{publicMutex();~Mutex();__forceinlinevoidAcquire(){EnterCriticalSection(&cs);}__forceinlinevoidRelease(){LeaveCriticalSection(&cs);}例如:线程操作函数。intAddCount(void){EnterCriticalSection(&cs);intnRet=m_nCount++;LeaveCriticalSection(&cs);returnnRet;}在函数AddCount里调用EnterCriticalSection和LeaveCriticalSection来互斥访问变量m_nCount。通过上面这种方法,就可以实现多线程按顺序地访问相同的变量__forceinlineboolAttemptAcquire(){一个线程也可以调用TryEnterCriticalSection函数来请求某个临界区的所有权,此时即使请求失败也不会被阻塞return;(TryEnterCriticalSection(&cs)==TRUEtruefalse);}protectedCRITICAL_SECTIONcs;临界区是一种防止多个线程同时执行一个特定代码节的机制};#endifMutex.cpp#includestdafx.h#includeMutex.hMutexMutex(){创建临界区对象InitializeCriticalSection(&cs);}Mutex~Mutex(){删除临界区对象DeleteCriticalSection(&cs);}ThreadPool.h#ifndef__THREADPOOL_H__#define__THREADPOOL_H__#includeThreadStarter.h#includeMutex.h#includewindows.h#includeassert.h#includesettypedefunsignedintuint;typedefsigned__intint;线程管理classThreadController{publicHANDLEhThread;uintthread_id;voidSetup(HANDLEh){hThread=h;}voidSuspend(){assert(GetCurrentThreadId()==thread_id);当线程做完任务或者现在想暂停线程运行,就需要使用SuspendThread来暂停线程的执行SuspendThread(hThread);}恢复线程的执行就是使用ResumeThread函数了voidResume(){assert(GetCurrentThreadId()!=thread_id);if(!ResumeThread(hThread)){DWORDle=GetLastError();printf(error揅e);}}voidJoin(){WaitForSingleObject函数用来检测hHandle事件的信号状态,当函数的执行时间超过dwMilliseconds就返回WaitForSingleObject(hThread,INFINITE);}uintGetId(){returnthread_id;}};structThread{ThreadBaseExecutionTarget;ThreadControllerControlInterface;MutexSetupMutex;线程的互斥boolDeleteAfterExit;};typedefstdsetThreadThreadSet;线程池类classCThreadPool{uint_threadsRequestedSinceLastCheck;uint_threadsFreedSinceLastCheck;uint_threadsExitedSinceLastCheck;uint_threadsToExit;int_threadsEaten;可用线程数量Mutex_mutex;ThreadSetm_activeThreads;正在执行任务线程对列ThreadSetm_freeThreads;可用线程对列publiThreadPool();voidIntegrityCheck();创建指定数量的线程并加到线程池voidStartup();销毁线程voidShutdown();boolThreadExit(Threadt);ThreadStartThread(ThreadBaseExecutionTarget);从线程池取得可用线程并执行任务voidExecuteTask(ThreadBaseExecutionTarget);voidShowStats();voidKillFreeThreads(uintcount);__forceinlinevoidGobble(){_threadsEaten=(int)m_freeThreads.size();}__forceinlineuintGetActiveThreadCount(){return(uint)m_activeThreads.size();}__forceinlineuintGetFreeThreadCount(){return(uint)m_freeThreads.size();}};externCThreadPoolThreadPool;线程池#endifThreadPool.cpp#includestdafx.h#includeThreadPool.h#includeprocess.hCThreadPoolThreadPool;CThreadPoolCThreadPool(){_threadsExitedSinceLastCheck=;_threadsRequestedSinceLastCheck=;_threadsEaten=;_threadsFreedSinceLastCheck=;}boolCThreadPoolThreadExit(Threadt){_mutex.Acquire();m_activeThreads.erase(t);if(_threadsToExit){--_threadsToExit;++_threadsExitedSinceLastCheck;if(t-DeleteAfterExit)m_freeThreads.erase(t);_mutex.Release();deletet;returnfalse;}enterthesuspendedpool++_threadsExitedSinceLastCheck;++_threadsEaten;stdsetThreaditeratoritr=m_freeThreads.find(t);if(itr!=m_freeThreads.end()){}m_freeThreads.insert(t);_mutex.Release();returntrue;}voidCThreadPoolExecuteTask(ThreadBaseExecutionTarget){Threadt;_mutex.Acquire();++_threadsRequestedSinceLastCheck;--_threadsEaten;从线程池夺取一个线程if(m_freeThreads.size())有可用线程{得到一个可用线程t=m_freeThreads.begin();把它从可用线程对列里删掉m_freeThreads.erase(m_freeThreads.begin());给这个线程一个任务t-ExecutionTarget=ExecutionTarget;恢复线程的执行t-ControlInterface.Resume();}else{创建一个新的线程并执行任务t=StartThread(ExecutionTarget);}把线程加到执行任务线程对列m_activeThreads.insert(t);_mutex.Release();}voidCThreadPoolStartup(){inti;inttcount=;for(i=;itcount;++i)StartThread(NULL);}voidCThreadPoolShowStats(){_mutex.Acquire();在这里输出线程池的状态_mutex.Release();}voidCThreadPoolKillFreeThreads(uintcount){_mutex.Acquire();Threadt;ThreadSetiteratoritr;uinti;for(i=,itr=m_freeThreads.begin();icount&&itr!=m_freeThreads.end();++i,++itr){t=itr;t-ExecutionTarget=NULL;t-DeleteAfterExit=true;++_threadsToExit;t-ControlInterface.Resume();}_mutex.Release();}voidCThreadPoolShutdown(){_mutex.Acquire();size_ttcount=m_activeThreads.size()+m_freeThreads.size();KillFreeThreads((uint)m_freeThreads.size());_threadsToExit+=(uint)m_activeThreads.size();for(ThreadSetiteratoritr=m_activeThreads.begin();itr!=m_activeThreads.end();++itr){if((itr)-ExecutionTarget)(itr)-ExecutionTarget-OnShutdown();}_mutex.Release();for(;;){_mutex.Acquire();if(m_activeThreads.size()m_freeThreads.size()){_mutex.Release();Sleep();continue;}break;}}staticunsignedlongWINAPIthread_proc(voidparam){Threadt=(Thread)param;t-SetupMutex.Acquire();uinttid=t-ControlInterface.GetId();boolht=(t-ExecutionTarget!=NULL);t-SetupMutex.Release();for(;;){if(t-ExecutionTarget!=NULL){if(t-ExecutionTarget-run())执行任务,返回true表示任务完成deletet-ExecutionTarget;t-ExecutionTarget=NULL;}if(!ThreadPool.ThreadExit(t)){Log.Debug(ThreadPool,Thread?ting.,tid);break;}else{if(ht)printf(ThreadPool线程%d正在等待新任务.,tid);t-ControlInterface.Suspend();暂停线程运行}}ExitThread();return;}ThreadCThreadPoolStartThread(ThreadBaseExecutionTarget){HANDLEh;Threadt=newThread;t-DeleteAfterExit=false;t-ExecutionTarget=ExecutionTarget;t-SetupMutex.Acquire();/*CreateThread(lpThreadAttributes是线程的属性,dwStackSize是线程的栈大小,lpStartAddress是线程函数的开始地址,lpParameter是传送给线程函数的参数,dwCreationFlags是创建线程标志,比如挂起线程,lpThreadId是标识这个线程的ID)*/h=CreateThread(NULL,,&thread_proc,(LPVOID)t,,(LPDWORD)&t-ControlInterface.thread_id);t-ControlInterface.Setup(h);t-SetupMutex.Release();returnt;}

  ⑿C++CreateThread函数如何传递this指针作为参数

  ⒀CreateThread的第三个参数是函数地址,不是调用函数,所以请把第三个参数的后面的括号去掉,直接写成。。。NULL,,CTcpServer::ThreadProc,(void*)。。。

  ⒁win程序创建线程用c语言库的_beginthread还是API的CreateThread哪种用的多

  ⒂让我们简单回顾一下历史。很早以前,是一个库用于单线程应用程序,另一个库用于多线程应用程序。之所以采用这个设计,是由于标准C运行库是在年左右发明的。要在很久很久之后,才会在操作系统上出现线程的概念。标准C运行库的发明者根本没有考虑到为多线程应用程序使用C运行库的问题。让我们用一个例子来了解可能遇到的问题。以标准C运行库的全局变量errno为例。有的函数会在出错时设置该变量。假定现在有这样的一个代码段:BOOLfFailure=(system(“NOTEPAD.EXEREADME.TXT“)==-);if(fFailure){switch(errno){caseE假设在调用了system函数之后,并在执行if语句之前,执行上述代码的线程被中断了。另外还假设,这个线程被中断后,同一个进程中的另一个线程开始执行,而且这个新线程将执行另一个C运行库函数,后者设置了全局变量errno。当CPU后来被分配回第一个线程时,对于上述代码中的system函数调用,errno反映的就不再是正确的错误码。为了解决这个问题,每个线程都需要它自己的errno变量。此外,必须有某种机制能够让一个线程引用它自己的errno变量,同时不能让它去碰另一个线程的errno变量。这仅仅是证明了“标准C/C++运行库最初不是为多线程应用程序而设计”的众多例子中的一个。在多线程环境中会出问题的C/C++运行库变量和函数有errno,_doserrno,strtok,_wcstok,strerror,_strerror,tmpnam,tmpfile,asctime,_wasctime,gmtime,_ecvt和_fcvt等等。为了保证C和C++多线程应用程序正常运行,必须创建一个数据结构,并使之与使用了C/C++运行库函数的每个线程关联。然后,在调用C/C++运行库函数时,那些函数必须知道去查找主调线程的数据块,从而避免影响到其他线程。那么,系统在创建新的线程时,是如何知道要分配这个数据块的呢?答案是它并不知道。系统并不知道应用程序是用C/C++来写的,不知道你调用的函数并非天生就是线程安全的。保证线程安全是程序员的责任。创建新线程时,一定不要调用操作系统的CreateThread函数。相反,必须调用C/C++运行库函数_beginthreadex:unsignedlong_beginthreadex(void*security,unsignedstack_size,unsigned(*start_address)(void*),void*arglist,unsignedinitflag,unsigned*thrdaddr);_beginthreadex函数的参数列表与CreateThread函数的一样,但是参数名称和类型并不完全一样。这是因为Microsoft的C/C++运行库开发组认为,C/C++运行库函数不应该对Windows数据类型有任何依赖。_beginthreadex函数也会返回新建线程的句柄,就像CreateThread那样。所以,如果已经在自己的源代码中调用了CreateThread函数,可以非常方便地用_beginthreadex来全局替换所有CreateThread。但是,由于数据类型并不完相同,所以可能还必须执行一些类型转换,以便顺利地通过编译。为了简化这个工作,我创建了一个名为chBEGINTHREADEX的宏,并在自己的源代码中使用:typedefunsigned(__stdcall*PTHREAD_START)(void*);#definechBEGINTHREADEX(psa,cbStack,pfnStartAddr,pvParam,fdwCreate,pdwThreadID)((HANDLE)_beginthreadex((void*)(psa),(unsigned)(cbStackSize),(PTHREAD_START)(pfnStartAddr),(void*)(pvParam),(unsigned)(dwCreateFlags),(unsigned*)(pdwThreadID)))根据Microsoft为C/C++运行库提供的源代码,很容易看出_beginthreadex能而CreateThread不能做的事情。事实上,在搜索了VisualStudio安装文件夹后,我在《ProgramFiles》MicrosoftVisualStudioVrtsrcThreadex.c中找到了_beginthreadex的源代码。为节省篇幅,这里没有全部照抄一遍。相反,我在这里提供了该函数的伪代码版本,强调了其中最有意思的地方:uintptr_t__cdecl_beginthreadex(void*psa,unsignedcbStackSize,unsigned(__stdcall*pfnStartAddr)(void*),void*pvParam,unsigneddwCreateFlags,unsigned*pdwThreadID){_ptiddataptd;//Pointertothread’sdatablockuintptr_tthdl;//Thread’shandle//Allocatedatablockforthenewthread.if((ptd=(_ptiddata)_calloc_crt(,sizeof(struct_tiddata)))==NULL)gotoerror_return;//Initializethedatablock.initptd(ptd);//Savethedesiredthreadfunctionandtheparameter//wewantittogetinthedatablock.ptd-》_initaddr=(void*)pfnStartAddr;ptd-》_initarg=pvParam;ptd-》_thandle=(uintptr_t)(-);//Createthenewthread.thdl=(uintptr_t)CreateThread((LPSECURITY_ATTRIBUTES)psa,cbStackSize,_threadstartex,(PVOID)ptd,dwCreateFlags,pdwThreadID);if(thdl==){//Threadcouldn’tbecreated,cleanupandreturnfailure.gotoerror_return;}//ThreadcreatedOK,returnthehandleasunsignedlong.return(thdl);()ismappedintoerrnocorrespondingvalues//ifsomethingwronghappenedinCreateThread._free_crt(ptd);return((uintptr_t)L);}对于_beginthreadex函数,以下几点需要重点关注。?每个线程都有自己的专用_tiddata内存块,它们是从C/C++运行库的堆(heap上分配的。?传给_beginthreadex的线程函数的地址保存在_tiddata内存块中。(_tiddata结构在Mtdll.h文件的C++源代码中。)纯粹是为了增加趣味性,我在下面重现了这个结构。要传入_beginthreadex函数的参数也保存在这个数据块中。?_beginthreadex确实会在内部调用CreateThread,因为操作系统只知道用这种方式来创建一个新线程。?CreateThread函数被调用时,传给它的函数地址是_threadstartex(而非pfnStartAddr。另外,参数地址是_tiddata结构的地址,而非pvParam。?如果一切顺利,会返回线程的句柄,就像CreateThread那样。任何操作失败,会返回。struct_tiddata{unsignedlong_tid;/*threadID*/unsignedlong_thandle;/*threadhandle*/int_terrno;/*errnovalue*/unsignedlong_tdoserrno;/*_doserrnovalue*/unsignedint_fpds;/*FloatingPointdatasegment*/unsignedlong_holdrand;/*rand()seedvalue*/char*_token;/*ptrtostrtok()token*/wchar_t*_wtoken;/*ptrtowcstok()token*/unsignedchar*_mtoken;/*ptrto_mbstok()token*//*followingpointersgetmalloc’datruntime*/char*_errmsg;/*ptrtostrerror()/_strerror()buff*/wchar_t*_werrmsg;/*ptrto_wcserror()/__wcserror()buff*/char*_namebuf;/*ptrtotmpnam()buffer*/wchar_t*_wnamebuf;/*ptrto_wtmpnam()buffer*/char*_namebuf;/*ptrtotmpfile()buffer*/wchar_t*_wnamebuf;/*ptrto_wtmpfile()buffer*/char*_asctimebuf;/*ptrtoasctime()buffer*/wchar_t*_wasctimebuf;/*ptrto_wasctime()buffer*/void*_gmtimebuf;/*ptrtogmtime()structure*/char*_cvtbuf;/*ptrtoecvt()/fcvtbuffer*/unsignedchar_con_ch_buf;/*ptrtoputch()buffer*/unsignedshort_ch_buf_used;/*ifthe_con_ch_bufisused*//*followingfieldsareneededby_beginthreadcode*/void*_initaddr;/*initialuserthreadaddress*/void*_initarg;/*initialuserthreadargument*//*followingthreefieldsareneededtosupportsignalhandlingandruntimeerrors*/void*_pxcptacttab;/*ptrtoexception-actiontable*/void*_tpxcptinfoptrs;/*ptrtoexceptioninfopointers*/int_tfpecode;/*floatpointexceptioncode*//*pointertothecopyofthemultibytecharacterinformationusedbythethread*/pthreadmbcinfoptmbcinfo;/*pointertothecopyofthelocaleinformationusedbythethread*/pthreadlocinfoptlocinfo;int_ownlocale;/*if,thisthreadownsitsownlocale*//*followingfieldisneededbyNLGroutines*/unsignedlong_NLG_dwCode;/**Per-ThreaddataneededbyC++ExceptionHandling*/void*_terminate;/*terminate()routine*/void*_unexpected;/*unexpected()routine*/void*_translator;/*S.E.translator*/void*_purecall;/*calledwhenpurevirtualhappens*/void*_curexception;/*currentexception*/void*_curcontext;/*currentexceptioncontext*/int_ProcessingThrow;/*foruncaught_exception*/void*_curexcspec;/*forhandlingexceptionsthrownfromstd::unexpected*/#ifdefined(_M_IA)||defined(_M_AMD)void*_pExitContext;void*_pUnwindContext;void*_pFrameInfoChain;unsigned__int_ImageBase;#ifdefined(_M_IA)unsigned__int_TargetGp;#endif/*defined(_M_IA)*/unsigned__int_ThrowImageBase;void*_pForeignException;#elifdefined(_M_IX)void*_pFrameInfoChain;#endif/*defined(_M_IX)*/_setloc_struct_setloc_data;void*_encode_ptr;/*EncodePointer()routine*/void*_decode_ptr;/*DecodePointer()routine*/void*_reserved;/*nothing*/void*_reserved;/*nothing*/void*_reserved;/*nothing*/int_cxxReThrow;/*SettoTrueifit’sarethrownC++Exception*/unsignedlong__initDomain;/*initialdomainusedby_beginthreadformanagedfunction*/};typedefstruct_tiddata*_ptiddata;为新线程分配并初始化_tiddata结构之后,接着应该知道这个结构是如何与线程关联的。来看看_threadstartex函数(它也在C/C++运行库的Threadex.c文件中)。下面是我为这个函数及其helper函数__callthreadstartex编写的伪代码版本:staticunsignedlongWINAPI_threadstartex(void*ptd){//Note:ptdistheaddressofthisthread’stiddatablock.//Associatethetiddatablockwiththisthreadso//_getptd()willbeabletofinditin_callthreadstartex.TlsSetValue(__tlsindex,ptd);//SavethisthreadIDinthe_tiddatablock.((_ptiddata)ptd)-》_tid=GetCurrentThreadId();//Initializefloating-pointsupport(codenotshown).//callhelperfunction._callthreadstartex();//Wenevergethere;thethreaddiesin_callthreadstartex.return(L);}staticvoid_callthreadstartex(void){_ptiddataptd;/*pointertothread’s_tiddatastruct*///getthepointertothreaddatafromTLSptd=_getptd();//WrapdesiredthreadfunctioninSEHframeto//handlerun-timeerrorsandsignalsupport.__try{//Calldesiredthreadfunction,passingitthedesiredparameter.//Passthread’sexitcodevalueto_endthreadex._endthreadex(((unsigned(WINAPI*)(void*))(((_ptiddata)ptd)-》_initaddr))(((_ptiddata)ptd)-》_initarg));}__except(_XcptFilter(GetExceptionCode(),GetExceptionInformation())){//TheCrun-time’sexceptionhandlerdealswithrun-timeerrors//andsignalsupport;weshouldnevergetithere._exit(GetExceptionCode());}}关于_threadstartex函数,要注意以下重点:?新的线程首先执行RtlUserThreadStart(在NTDLL.dll文件中),然后再跳转到_threadstartex。?_threadstartex惟一的参数就是新线程的_tiddata内存块的地址。?TlsSetValue是一个操作系统函数,它将一个值与主调线程关联起来。这就是所谓的线程本地存储(ThreadLocalStorage,TLS),详情参见第章。_threadstartex函数将_tiddata内存块与新建线程关联起来。?在无参数的helper函数_callthreadstartex中,一个SEH帧将预期要执行的线程函数包围起来。这个帧处理着与运行库有关的许多事情——比如运行时错误(如抛出未被捕捉的C++异常)——和C/C++运行库的signal函数。这一点相当重要。如果用CreateThread函数新建了一个线程,然后调用C/C++运行库的signal函数,那么signal函数不能正常工作。?预期要执行的线程函数会被调用,并向其传递预期的参数。前面讲过,函数的地址和参数由_beginthreadex保存在TLS的_tiddata数据块中;并会在_callthreadstartex中从TLS中获取。?线程函数的返回值被认为是线程的退出代码。注意_callthreadstartex不是简单地返回到_threadstartex,继而到RtlUserThreadStart;如果是那样的话,线程会终止运行,其退出代码也会被正确设置,但线程的_tiddata内存块不会被销毁。这会导致应用程序出现内存泄漏。为防止出现这个问题,会调用_endthreadex(也是一个C/C++运行库函数,并向其传递退出代码。最后一个需要关注的函数是_endthreadex(也在C运行库的Threadex.c文件中)。下面是我编写的该函数的伪代码版本:void__cdecl_endthreadex(unsignedretcode){_ptiddataptd;//Pointertothread’sdatablock//Cleanupfloating-pointsupport(codenotshown).//Gettheaddressofthisthread’stiddatablock.ptd=_getptd_noexit();//Freethetiddatablock.if(ptd!=NULL)_freeptd(ptd);//Terminatethethread.ExitThread(retcode);}对于_endthreadex函数,要注意几下几点:?C运行库的_getptd_noexit函数在内部调用操作系统的TlsGetValue函数,后者获取主调线程的tiddata内存块的地址。?然后,此数据块被释放,调用操作系统的ExitThread函数来实际地销毁线程。当然,退出代码会被传递,并被正确地设置。在本章早些时候,我曾建议大家应该避免使用ExitThread函数。这是千真万确的,而且我在这里并不打算自相矛盾。前面说过,此函数会杀死主调线程,而且不允许它从当前执行的函数返回。由于函数没有返回,所以构造的任何C++对象都不会被析构。现在,我们又有了不调用ExitThread函数的另一个理由:它会阻止线程的_tiddata内存块被释放,使应用程序出现内存泄漏(直到整个进程终止。Microsoft的C++开发团队也意识到,总有一些开发人员喜欢调用ExitThread。所以,他们必须使这成为可能,同时尽可能避免应用程序出现内存泄漏的情况。如果真的想要强行杀死自己的线程,可以让它调用_endthreadex(而不是ExitThread)来释放线程的_tiddata块并退出。不过,我并不鼓励你调用_endthreadex。现在,你应该理解了C/C++运行库函数为什么要为每一个新线程准备一个独立的数据块,而且应该理解了_beginthreadex如何分配和初始化此数据块,并将它与新线程关联起来。另外,你还应理解了_endthreadex函数在线程终止运行时是如何释放该数据块的。一旦这个数据块被初始化并与线程关联,线程调用的任何需要“每线程实例数据”的C/C++运行库函数都可以轻易获取主调线程的数据块的地址(通过TlsGetValue),并操纵线程的数据。这对函数来说是没有问题的。但是,对于errno之类的全局变量,它又是如何工作的呢?errno是在标准Cheaders中定义的,如下所示:_CRTIMPexternint*__cdecl_errno(void);#defineerrno(*_errno())int*__cdecl_errno(void){_ptiddataptd=_getptd_noexit();if(!ptd){return&ErrnoNoMem}else{return(&ptd-》_terrno);}}任何时候引用errno,实际都是在调用内部的C/C++运行库函数_errno。该函数将地址返回给“与主调线程关联的数据块”中的errno数据成员。注意,errno宏被定义为获取该地址的内容。这个定义是必要的,因为很可能写出下面这样的代码:int*p=&errnoif(*p==ENOMEM){...}如果内部函数_errno只是返回errno的值,上述代码将不能通过编译。C/C++运行库还围绕特定的函数放置了同步原语(synchronizationprimitives。例如,如果两个线程同时调用malloc,堆就会损坏。C/C++运行库函数阻止两个线程同时从内存堆中分配内存。具体的办法是让第个线程等待,直至第个线程从malloc函数返回。然后,才允许第个线程进入。(线程同步将在第章和第章详细讨论。)显然,所有这些额外的工作影响了C/C++运行库的多线程版本的性能。C/C++运行库函数的动态链接版本被写得更加泛化,使其可以被使用了C/C++运行库函数的所有运行的应用程序和DLL共享。因此,库只有一个多线程版本。由于C/C++运行库是在一个DLL中提供的,所以应用程序(.exe文件)和DLL不需要包含C/C++运行库函数的代码,所以可以更小一些。另外,如果Microsoft修复了C/C++运行库DLL的任何bug,应用程序将自动获得修复。就像你期望的一样,C/C++运行库的启动代码为应用程序的主线程分配并初始化了一个数据块。这样一来,主线程就可以安全地调用任何C/C++运行库函数。当主线程从其入口函数返回的时候,C/C++运行库函数会释放关联的数据块。此外,启动代码设置了正确的结构化异常处理代码,使主线程能成功调用C/C++运行库的signal函数。..用_beginthreadex而不要用CreateThread创建线程你可能会好奇,假如调用CreateThread而不是C/C++运行库的_beginthreadex来创建新线程,会发生什么呢?当一个线程调用一个需要_tiddata结构的C/C++运行库函数时,会发生下面的情况。(大多数C/C++运行库函数都是线程安全的,不需要这个结构。)首先,C/C++运行库函数尝试取得线程数据块的地址(通过调用TlsGetValue)。如果NULL被作为_tiddata块的地址返回,表明主调线程没有与之关联的_tiddata块。在这个时候,C/C++运行库函数会为主调线程分配并初始化一个_tiddata块。然后,这个块会与线程关联(通过TlsSetValue),而且只要线程还在运行,这个块就会一直存在并与线程关联。现在,C/C++运行库函数可以使用线程的_tiddata块,以后调用的任何C/C++运行库函数也都可以使用。当然,这是相当诱人的,因为线程(几乎可以顺畅运行。但事实上,问题还是有的。第一个问题是,假如线程使用了C/C++运行库的signal函数,则整个进程都会终止,因为结构化异常处理(SEH帧没有就绪。第二个问题是,假如线程不是通过调用_endthreadex来终止的,数据块就不能被销毁,从而导致内存泄漏。(对于一个用CreateThread函数来创建的线程,谁会调用_endthreadex呢?)

  ⒃MFC中如何在一个按钮中循环一个事件,在点击另一按钮时结束这个事件

  ⒄给你个简单的小例子吧实现你的功能。

  ⒅建立一个MFC工程,上面放个按钮:第一个按钮是启动循环;第二个按钮是结束循环;

  ⒆//CreateThreadexDlg.cpp:实现文件//

  ⒇#include“stdafx.h“#include“CreateThreadex.h“#include“CreateThreadexDlg.h“#include“afxdialogex.h“

  ⒈#ifdef_DEBUG#definenewDEBUG_NEW#endif

  ⒉boolbInLoop=true;//------------》定义一个全局变量用于控制循环//用于应用程序“关于”菜单项的CAboutDlg对话框

  ⒊classCAboutDlg:publiDialogEx{public:?CAboutDlg();

  ⒋在第一个按钮(启动循环)事件里面加入如下代码:

  ⒌//单击此按钮启动一个线程用于实现循环voidreateThreadexDlg::OnBnClickedButton(){?//TODO:在此添加控件通知处理程序代码?CreateThread(NULL,,(LPTHREAD_START_ROUTINE)startlop,NULL,,);}③在第一个按钮上面定义一个线程函,此函数用于当满足条件实现死循环,不满足条件跳出循环从而关闭线程;

  ⒍LPTHREAD_START_ROUTINEstartlop(void*){?while(bInLoop)//逻辑条件?{??MessageBoxW(NULL,L“hello“,L“test“,MB_OK);??Sleep();?}?return;}

  ⒎关闭循环,点击此按钮将全局变量赋予假,则结束循环

  ⒏voidreateThreadexDlg::OnBnClickedButton(){?//TODO:在此添加控件通知处理程序代码

  ⒐bInLoop=false;

  ⒑很简单的一个线程例子,当然方法众多,希望对初学的你有帮助,请采纳,谢谢

  ⒒用C语言开多线程,想让多个相同的子线程同时运行,怎么实现

  ⒓工作线程是处理后台工作的,创建一个线程非常简单,只需要两步:实线线程函数和开始线程.不需要由CWinThread派生类,你可以不加修改地使用CWinThread。AfxBeginThread有两种形式,一种是用来创建用户界面线程的,另一种就是用来创建工作线程的.为了开始执行线程,只需要向AfxBeginThread提供下面的参数就可以了..线程函数的地址.传送到线程函数的参数.(可选的线程的优先级,可参阅::SetThreadPriority.(可选的线程开始时候的状态,可设置为CREATE_SUSPENEDE.(可选的线程的安全属性,请参阅SECURITY_ATTRIBUTES实例代码UINTThreadProc(LPVOIDpParam){return;//线程成功完成}CWinThread*AfxBeginThreadProc,//线程函数地址LPVOIDpParam,//线程参数intnPriority=THREAD+PRIORITY_NOMAL,//线程优先级intnStackSize=,//线程堆栈大小,默认为MDWORDdwCreateFlags=,LPSECURITY_ATTRIBUTESlpSecurityAttrs=NULL);

  ⒔PP是peer-to-peer的缩写,peer在英语里有“(地位、能力等同等者“、“同事“和“伙伴“等意义。这样一来,PP也就可以理解为“伙伴对伙伴“的意思,或称为对等联网。目前人们认为其在加强网络上人的交流、文件交换、分布计算等方面大有前途.简单的说,PP直接将人们联系起来,让人们通过互联网直接交互。PP使得网络上的沟通变得容易、更直接共享和交互,真正地消除中间商。PP就是人可以直接连接到其他用户的计算机、交换文件,而不是像过去那样连接到服务器去浏览与下载。PP另一个重要特点是改变互联网现在的以大网站为中心的状态、重返“非中心化“,并把权力交还给用户。PP看起来似乎很新,但是正如BC、BB是将现实世界中很平常的东西移植到互联网上一样,PP并不是什么新东西。在现实生活中我们每天都按照PP模式面对面地或者通过电话交流和沟通。即使从网络看,PP也不是新概念,PP是互联网整体架构的基础。互联网最基本的协议TCP/IP并没有客户机和服务器的概念,所有的设备都是通讯的平等的一端。在十年之前,所有的互联网上的系统都同时具有服务器和客户机的功能。当然,后来发展的那些架构在TCP/IP之上的软件的确采用了客户机/服务器的结构:浏览器和Web服务器,邮件客户端和邮件服务器。但是,对于服务器来说,它们之间仍然是对等联网的。以email为例,互联网上并没有一个巨大的、唯一的邮件服务器来处理所有的email,而是对等联网的邮件服务器相互协作把email传送到相应的服务器上去。另外用户之间email则一直对等的联络渠道。事实上,网络上现有的许多服务可以归入PP的行列。即时讯息系统譬如ICQ、AOLInstantMessenger、YahooPager、微软的MSNMessenger以及国内的OICQ是最流行的PP应用。它们允许用户互相沟通和交换信息、交换文件。用户之间的信息交流不是直接的,需要有位于中心的服务器来协调。但这些系统并没有诸如搜索这种对于大量信息共享非常重要的功能,这个特征的缺乏可能正为什么即时讯息出现很久但是并没有能够产生如Napster这样的影响的原因之一。

您可能感兴趣的文章:

相关文章