线程函数java判断线程执行完毕毕后,线程(空间)会不会自动释放

查看: 4889|回复: 2
多线程是否运行完毕后是否真的会自己关闭线程释放资源
阅读权限30
结帖率: (30/37)
都说线程运行完会自己关闭,为什么线程结束后,强制结束线程 还会成功呢。 按道理执行强制结束线程和关闭句柄,不应该是 强制结束失败 关闭句柄成功吗
&&窗口程序集名保 留&&保 留备 注窗口程序集_启动窗口&&&变量名类 型数组备 注 子程序线程句柄整数型&&_按钮1_被单击&&启动线程 (&子程序1, , 子程序线程句柄)子程序1&&a整数型&&计次循环首 (5, )a = a + 1调试输出 (a)计次循环尾 ()_按钮2_被单击&&如果 (强制结束线程 (子程序线程句柄) = 真)调试输出 (“结束线程成功”)调试输出 (“结束线程失败”)如果 (关闭线程句柄 (子程序线程句柄) = 真)调试输出 (“关闭句柄成功”)调试输出 (“关闭句柄失败”)EThread多线程支持库
spec特殊功能支持库
.版本 2
.支持库 EThread
.支持库 spec
.程序集 窗口程序集_启动窗口
.程序集变量 子程序线程句柄, 整数型
.子程序 _按钮1_被单击
启动线程 (&子程序1, , 子程序线程句柄)
.子程序 子程序1
.局部变量 a, 整数型
.计次循环首 (5, )
a = a + 1
调试输出 (a)
.计次循环尾 ()
.子程序 _按钮2_被单击
.如果 (强制结束线程 (子程序线程句柄) = 真)
调试输出 (“结束线程成功”)
调试输出 (“结束线程失败”)
.如果 (关闭线程句柄 (子程序线程句柄) = 真)
调试输出 (“关闭句柄成功”)
调试输出 (“关闭句柄失败”)
你先关了线程句柄,强制结束线程就不行了。。线程句柄就像一把钥匙,你通过这把钥匙去控制线程。。如果你把钥匙丢了,线程你就控制不了
回答提醒:如果本帖被关闭无法回复,您有更好的答案帮助楼主解决,请发表至
可获得加分喔。友情提醒:本版被采纳的主题可在
帖子申请荣誉值,获得 1点 荣誉值,荣誉值可兑换终身vip用户组哦。快捷通道: →
阅读权限165
你先关了线程句柄,强制结束线程就不行了。。线程句柄就像一把钥匙,你通过这把钥匙去控制线程。。如果你把钥匙丢了,线程你就控制不了
热心帮助他人,荣誉+1,希望继续努力(*^__^*) 嘻嘻!
您可以选择打赏方式支持他
阅读权限30
结帖率: (30/37)
你先关了线程句柄,强制结束线程就不行了。。线程句柄就像一把钥匙,你通过这把钥匙去控制线程。。如果你把 ...
如何判断线程执行后自己关闭了呢。你说的这个道理我是明白的。
您可以选择打赏方式支持他
拒绝任何人以任何形式在本论坛发表与中华人民共和国法律相抵触的言论,本站内容均为会员发表,并不代表精易立场!
揭阳精易科技有限公司申明:我公司所有的培训课程版权归精易所有,任何人以任何方式翻录、盗版、破解本站培训课程,我们必将通过法律途径解决!
公司简介:揭阳市揭东区精易科技有限公司致力于易语言教学培训/易语言学习交流社区的建设与软件开发,多年来为中小企业编写过许许多多各式软件,并把多年积累的开发经验逐步录制成视频课程供学员学习,让学员全面系统化学习易语言编程,少走弯路,减少对相关技术的研究与摸索时间,从而加快了学习进度!
防范网络诈骗,远离网络犯罪
违法和不良信息举报电话,QQ: ,,邮箱:@
Powered by
粤公网安备 25Windows线程创建、退出及资源释放 - CSDN博客
Windows线程创建、退出及资源释放
可以通过以下几种方法创建一个线程:
1、CreateThread
2、_beginthread
3、_beginthreadex
4、AfxBeginThread
--------------------------------------------------------------------------------------
<span style="font-size:14 color:#、CreateThread
HANDLE CreateThread( LPSECURITY_ATTRIBUTES lpThreadAttributes, // SD  
SIZE_T dwStackSize, // initial stack size  
LPTHREAD_START_ROUTINE lpStartAddress, // thread function  
LPVOID lpParameter, // thread argument  
DWORD dwCreationFlags, // creation option  
LPDWORD lpThreadId // thread identifier);
lpThreadAttributes:指向SECURITY_ATTRIBUTES型态的结构的指针。在Windows 98中忽略该参数。在Windows NT中,NULL使用默认安全性,不可以被子线程继承,否则需要定义一个结构体将它的bInheritHandle成员初始化为TRUE
dwStackSize:设置初始栈的大小,以字节为单位,如果为0,那么默认将使用与调用该函数的线程相同的栈空间大小。任何情况下,Windows根据需要动态延长堆栈的大小。
lpStartAddress:指向线程函数的指针,必须以下列形式声明:
DWORD WINAPI ThreadProc (LPVOID lpParam) ,&#26684;式不正确将无法调用成功。//也可以直接调用void类型//但lpStartAddress要这样通过LPTHREAD_START_ROUTINE转换如:(LPTHREAD_START_ROUTINE)MyVoid//然后在线程声明为:void MyVoid(){ }
lpParameter:向线程函数传递的参数,是一个指向结构的指针,不需传递参数时,为NULL。
dwCreationFlags :线程标志,可取&#20540;如下:
--------------------------------------------------------------------------------------
CREATE_SUSPENDED(0x):创建一个挂起的线程,
0:表示创建后立即激活。
STACK_SIZE_PARAM_IS_A_RESERVATION(0x):dwStackSize参数指定初始的保留堆栈的大小,否则,dwStackSize指定提交的大小。该标记&#20540;在Windows 2000/NT and Windows Me/98/95上不支持。
--------------------------------------------------------------------------------------
lpThreadId:保存新线程的id。
返回&#20540;:
函数成功,返回线程句柄;函数失败返回false。若不想返回线程ID,设置&#20540;为NULL。
PMYDATA pData;
pData = (PMYDATA)HeapAlloc(GetProcessHeap(), HEAP_ZERO_MEMORY,
sizeof(MYDATA));
if( pData == NULL )
ExitProcess(2);
// Generate unique data for each thread.
pData-&val1 = i;
pData-&val2 = i&#43;100;
HANDLE hThread = CreateThread( NULL, // default security attributes
0, // use default stack size
ThreadProc, // thread function
pData, // argument to thread function
0, // use default creation flags
&dwThreadId[i]); // returns the thread identifier
// Check the return value for success.
if (hThread == NULL)
ExitProcess(i);
<span style="font-size:14 color:#、_beginthread
函数原型:
uintptr_t _beginthread( void( *start_address )( void * ),
unsigned stack_size,
void *arglist
start_address:新线程的起始地址 ,指向新线程调用的函数的起始地址
stack_size:新线程的堆栈大小,可以为0
arglist:&传递给线程的参数列表,无参数是为NULL&
返回&#20540;:
假如成功,函数将返回一个处理信息对这个新创建的线程。如果失败_beginthread将返回-1。
引用头文件:
#include&&process.h&
m_hThreadHandle = HANDLE(_beginthread(CAppLog::LogProcThread, 0, this));
3、_beginthreadex
函数原型(MSDN):
unsigned long _beginthreadex(
void *security,&
unsigned stack_size,&
unsigned ( __stdcall *start_address )( void * ),&
void *arglist,&
unsigned initflag,&
unsigned *thrdaddr );
security:安全属性,NULL为默认安全属性
stack_size:指定线程堆栈的大小。如果为0,则线程堆栈大小和创建它的线程的相同。一般用0
start_address
:指定线程函数的地址,也就是线程调用执行的函数地址(用函数名称即可,函数名称就表示地址)
arglist:传递给线程的参数的指针,可以通过传入对象的指针,在线程函数中再转化为对应类的指针
initflag:线程初始状态,0:立即运行;CREATE_SUSPEND:suspended(悬挂)
:用于记录线程ID的地址
返回&#20540;:
与_beginthread()不同的是:_beginthread返回-1表示失败, 而_beginthreadex()返回0表示失败!
引用头文件:
#include &process.h&
HANDLE hThread;
hThread = (HANDLE)_beginthreadex( NULL, 0, &SIPVoIPLink::ReceivingThrd, (LPVOID)this, 0, &threadID );
if(hThread == NULL) return false;
4、AfxBeginThread
这个看起来就直接了,MFC封装的函数。
在MFC中,用户界面线程和工作者线程都是由AfxBeginThread创建的,MFC提供了两个重载版的AfxBeginThread,一个用于用户界面线程,另一个用于工作者线程,分别有如下的原型和过程:
原型一(用户界面线程):
CWinThread* AFXAPI AfxBeginThread( CRuntimeClass* pThreadClass,
int nPriority,
UINT nStackSize,
DWORD dwCreateFlags,
LPSECURITY_ATTRIBUTES lpSecurityAttrs)
pThreadClass:从CWinThread派生的RUNTIME_CLASS类;
nPriority:指定线程优先级,如果为0,则与创建该线程的线程相同;
nStackSize:指定线程的堆栈大小,如果为0,则与创建该线程的线程相同;
dwCreateFlags:一个创建标识,如果是CREATE_SUSPENDED,则在悬挂状态创建线程,在线程创建后线程挂起,否则线程在创建后开始线程的执行。
lpSecurityAttrs:表示线程的安全属性,NT下有用。
原型二(工作线程)
CWinThread* AfxBeginThread( AFX_THREADPROC pfnThreadProc,
LPVOID lParam,
int nPriority = THREAD_PRIORITY_NORMAL,
UINT nStackSize = 0,
DWORD dwCreateFlags = 0,
LPSECURITY_ATTRIBUTES lpSecurityAttrs = NULL
);//用于创建工作者线程
pfnThreadProc : 线程的入口函数,声明一定要如下: UINT MyThreadFunction(LPVOID pParam),不能设置为NULL;
pParam : 传递入线程的参数,注意它的类型为:LPVOID,所以我们可以传递一个结构体入线程.
nPriority : 线程的优先级,一般设置为 0 .让它和主线程具有共同的优先级.
nStackSize : 指定新创建的线程的栈的大小.如果为 0,新创建的线程具有和主线程一样的大小的栈
dwCreateFlags : 指定创建线程以后,线程有怎么样的标志.可以指定两个&#20540;:
--------------------------------------------------------------------------------------
CREATE_SUSPENDED : 线程创建以后,会处于挂起状态,直到调用:ResumeThread
0 : 创建线程后就开始运行.
--------------------------------------------------------------------------------------
lpSecurityAttrs : 指向一个 SECURITY_ATTRIBUTES 的结构体,用它来标志新创建线程的安全性.如果为 NULL,
返回&#20540;:
一个指向新线程的线程对象的指针
以上几种创建线程的区别
其实windows只提供了一个创建线程的方法,就是CreateThread,后面三个函数都是由CreateThread间接得到。
<span style="color:#、_beginthread和_beginthreadex的区别
首先我们看看这两个函数都干了什么:
uintptr_r __cdecl _beginthreadex(...)
//为即将创建的线程分配一个数据结构_ptiddata ptd(per-thread data)
//初始化这个数据结构,其中ptd-&_thandle = (uintptr_t)(-1)
//如果初始化失败,返回(uintptr_t)(0) [_beginthread返回-1]
//用传进来的参数,调用CreateThread
//如果创建成功返回CreateThread返回的代码
//如果创建失败则释放ptd,并返回(uintptr_t)(0) [_beginthread返回-1,而CreateThread失败返回0,非-1]
然后再看看这两个函数有什么不同
(1)参数列表不同, ex版本的参数和CreateThread差不多:
(2)二者在初始化ptd失败时返回的&#20540;不同
(3)_beginthread的参数缺少安全描述符. 而且它是创建线程的时候先以挂起状态创建 (CreateThread会填充ptd-&_thandle和ptd-&_tid) 然后再ResumeThread。_beginthread是根据传进来的参数创建线程
(4)失败返回&#20540;不同,ex版本的与Windows API CreateThread返回&#20540;是一直的,这也是提倡使用后者的原因之一
<span style="color:#、CreateThread()、_beginthreadex()及、AfxBeginThread()
CreateThread时Windows API接口函数,_beginthreadex函数是C/C&#43;&#43;运行库提供的函数,从 _beginthreadex函数的源代码,可以看出它的主要动作是:增加了一个名为ptd的_ptiddata的结构的处理,然后在调用CreateThread函数。_ptiddata是每个线程都拥有自己的专用的数据结构。AfxBeginThread是MFC封装的启动线程的函数,里面包含了很多和MFC相关的启动信息,而且封装了一些常用的操作,使用起来也比较简便。而用另外两个函数就需要程序员对类型,安全性检查进行更多的思考!
----------------------------------------------------------------------------------------
线程退出方式
1.线程函数返回
2.线程通过调用ExitThread终止自己(自己调用)
3.TerminateThread(本进程或其它进程的线程调用)
4.包含线程的进程终止
第一种是最完美的退出方式,应尽可能使用第一种方法以避免资源泄漏!
----------------------------------------------------------------------------------------
线程资源释放
必须明白,一个线程终止和线程资源释放那个不是一个概念。当一个线程以上面几种退出方式终止之后,线程对象及资源还存在于内存中,所以在线程终止之后还需要某种方式释放资源。
线程的释放方式与创建方式相关联:
CreateThread创建的线程应使用CloseHandle关闭。
_beginthread创建的线程应使用_endthread关闭。
_beginthreadex创建的线程应使用_endthreadex关闭。
AfxBeginThread创建的线程,如果是线程自己结束自己的线程(从CWinThread继承出来的)就用AfxEndThread。如果外部调用的话可以用PostThreadMessage(m_nThreadID, WM_QUIT,0,0);给这个线程发送消息,线程就会结束的,其中的m_nThreadID是线程ID。
线程资源的释放方法(常用):
<span style="color:#、如果对创建线程的引用不感兴趣,可在创建之后直接关闭句柄
void main(){ …… HANDLE hThread = CreateThread(NULL,0,ThreadFunc,NULL,0,NULL); CloseHandle(hThread); ……}
有些新手可能会有个疑问,为什么在刚创建完线程之后就把句柄关掉了?
要特别注意句柄和实例区别,句柄只是对实例对象的引用,用以操作对象。我们使用CreateThread创建了一个线程实例(可将线程整体看作一个对象),返回的线程句柄只是提供了一个方法(类&#20284;于指针)让我们可以访问或操作线程对象。我们这里只需要让创建的线程执行必要的代码段就好了,之后并不需要在对其进行任何操作,所以可以直接将其CloseHandle掉,关闭句柄只是切断了访问线程的方式罢了,线程还是存在并运行着的。
然而,只是这样解释还是有所偏差,因为在很多使用CloseHandle关闭其他对象句柄的操作中都会释放对象占用的资源,而对于线程,在调用CloseHandle之后并不会终止线程,也就不会立马释放线程资源(实现中线程还必须继续运行),调用CloseHandle之后系统会递减线程内核对象的使用计数,当线程执行完毕(线程函数执行完)之后也会递减此线程内核对象使用计数,当计数为0时才会释放线程资源!
反过来看,如果不使用CloseHandle关闭线程句柄,那么系统就会一直保持着对此线程内核对象的引用,这样,即使线程执行完毕,使用计数也不会为0,所以线程资源不会被释放。
因此,CloseHandle关闭线程句柄是释放线程资源的必要途径,但不会影响线程的正常运行!所以CloseHandle的调用位置非常灵活,即可在线程刚创建出来之后紧接着调用,也可以在线程执行完之后才调用!
<span style="color:#、外部线程WaitForSingleObject,发现线程中止运行后,释放线程相关的资源
void main(){& CThread thread; //一个自己定义的线程封装类,其中有一些成员变量,Start启动线程函数,Release释放资源函数。& thread.Start();& while(WaitForSingleObject(thread.m_hThread,1000) != WAIT_OBJECT_0) {&
//TerminateThread(thread.m_hThread);//在这里如果你不想再等待,可以中止它&
Sleep(1000);& }& thread.Release();}
这种方式的优点是对线程的全面的管理。
缺陷在于,如果要即时释放资源,必须有一个专门的外部的线程来不断的监视或管理线程运行状态。
<span style="color:#、线程退出时将自身资源释放
DWORD CALLBACK CThread::Thread(VOID *pParam) //线程的入口函数,static函数{& CThread *pthread = pP& ... //工作状态设置& pthread-&Execute()& ... //工作状态设置& pthread-&Release() //在所有的工作做完之后进行释放资源& return 0;}
这种方式的优点是对线程的资源的时机是最准确的,缺陷是必须要保证线程在需要退出时不会阻塞。
3、在实际的工作中,我们需要这多种方式的集合来实现我们的功能
如有个网络服务器,它给每个用户的新建一个线程来响应它的请求,如果用户退出,要保证相关资源要完整的释放,并且服务器本身有停止功能,停止时,不论有多少和用户交互,都必须中止所有线程,且释放所有资源。
要解决的子问题:
Q1:用户退出时资源释放:
解决方式:
(1)使用线程自身退出时释放的方式。
(2)使用一个外部监视线程,发现线程退出时释放相应的资源。
Q2:服务器停止时退出且释放所有线程:
设用户线程工作状态为停止,等待用户线程自然退出,如果没有退出,可能是阻塞状态。这时使用强制退出的方法。
释放所有用户线程相关的资源,可以根据资源的使用时间和方式(如某些Windows资源句柄),在线程退出之前的某时机进行释放。
下面是我实作的满足以上要求的自身退出方式释放资源的线程类的代码片段:
int CThread::Stop(){& m_RunningMutex.Lock(); //判断线程的工作状态,如果已经退出,直接返回& if(!m_bRunning)& {&
m_RunningMutex.Unlock();&
return ERR_VTHREAD_ALREAD_STOP;& }& m_RunningMutex.Unlock();& m_StopMutex.Lock();& m_bStop = True; &//设置线程的工作状态为停止& m_StopMutex.Unlock();#ifdef _WIN32& DWORD ThreadId = GetCurrentThreadId(); //判断是否线程自身退出,如果是,则返回,不能调用强制退出的方法#else& pthread_t ThreadId = pthread_self();#endif &if(ThreadId != m_ThreadID)& {&
m_dwExitMode = EXIT_BY_OTHER;& }& else& {&
m_dwExitMode = EXIT_BY_SELF;& }&if(m_dwExitMode == EXIT_BY_OTHER) //如果是服务器停止信号&{&
int nTryTime = 0;&
bool bStopTry =&
while( m_bRunning && !bStopTry) //尝试判断线程是否停止,最好使线程自然退出&
if (nTryTime & m_nStopWaitTime)&bStopTry =& &
Sleep(10000);& &
nTryTime &#43;&#43;;&
if(bStopTry) //如果线程没有退出,就强制退出#ifdef _WIN32& &
TerminateThread(m_hThread, DEF_EXIT_CODE);#else& &
pthread_cancel(m_ThreadID);#endif&
m_RunningMutex.Lock();&
m_bRunning = F&
m_RunningMutex.Unlock();&
Release(); //线程已经停止,释放所有私有次源,这行代码可以在服务器端,让此函数只执行停止功能& }& return 0;}//这是线程的主函数,它包含的释放自身私有资源的功能。#ifdef WIN32DWORD __stdcall CThread::ThreadProc( VOID *lpParameter )#elseVOID* CThread::ThreadProc( VOID *lpParameter )#endif{&BOOL bS#ifndef _WIN32& //pthread_detach(pthread_self());#endif& CThread* pThread = (CThread*)lpP& pThread-&m_RunningMutex.Lock();& pThread-&m_bRunning = True;& pThread-&m_RunningMutex.Unlock();& pThread-&m_StopMutex.Lock();& bStop = pThread-&m_bStop;& pThread-&m_StopMutex.Unlock();& if( !bStop)& {&
pThread-&Execute();& }& pThread-&m_RunningMutex.Lock();& pThread-&m_bRunning = False;& pThread-&m_RunningMutex.Unlock();& if(pThread-&m_dwExitMode == EXIT_BY_SELF) //在这里判断线程是否自身退出,如是:释放私有资源& {&
pThread-&Release(); &} &//退出线程,使线程以最自然的方式退出.& return 0;}
深入讨论几种创建线程的方式
(1)使用_beginthreadex创建的线程就不该用CloseHandle释放,因为,当用_beginThread来创建,而用CloseHandle来关闭线程时,这时复制的全局结构就不会被释放了,这就有了内存的泄漏。这就是很多资料所说的内存泄漏问题的真正的原因。
(2)不要在一个MFC程序中使用_beginthreadex()或CreateThread()。这句话的意思是由于AfxBeginThread()是MFC封装的启动线程的函数,里面包含了很多和MFC相关的启动信息,而且封装了一些常用的操作,使用起来也比较简便。而用另外两个函数就需要程序员对类型,安全性检查进行更多的思考!
(3)用_beginthreadex()函数应该是最佳选择,因为_beginthreadex()函数是CRun-timeLibrary中的函数,函数的参数和数据类型都是CRun-timeLibrary中的类型,这样在启动线程时就不需要进行Windows数据类型和CRun-timeLibrary中的数据类型之间的转化。减低了线程启动时的资源消耗和时间的消耗!
(4)在C程序中,几乎都要用到new和delete,难道只有使用_beginthreadex()?不,因为MFC也是C类库(只不过是Microsoft的C类库,不是标准的C类库),在MFC中也封装了new和delete两中运算符,所以用到new和delete的地方不一定非要使用_beginthreadex()函数,用其他两个函数都可以!其实在程序中使用上面的哪个函数并不是绝对的,书的作者只不过是提了一个更佳的搭配方法,我在MFC程序中也经常使用_beginthreadex()和CreateThread()这两个函数,运行的效果也没有多大的区别,有的时候只是需要你额外的进行一些类型检查和其他的一些转化操作,其余没有其他不妥!
创建线程只有一个方法是::CreateThread()。_beginthreadex()、AfxBeginThread()等内部都是调用这个函数的,因为操作系统只提供这一个接口C静态库比WINDOWS出来还早,就别提多线程了,所以他对多线程的支持不是很好,但后悔也来不急,但也不能怪人家。
(5)C运行库_beginthreadex()。他经过一些处理后,再调用CreateThread()如果要强制结束的话也最好用_endthreadex结束,因为他也要一些处理。 总结上面的内容,当然《Windows核心编程》上面得说法是比较权威的。所以,在对线程的结构、运行还不是很了解的时候最好还是按照书上的来。这样能够避免一些可能出现的莫名奇妙的错误,也省去的一些其他结构处理的考虑。当你清楚地知道线程的结构与运行机制,以及了解各个函数对CreateThread函数的封装的时候,大概那时候就能够应用自如了
本文已收录于以下专栏:
相关文章推荐
java使用JInvoke调用windows API
使用jinvoke调用windowsAPI。jna使用比较麻烦,需要写c代码和参数转换,jinvoke的使用就像jdk中的包一样。&#160;
官网使用...
使用VC编程来操纵Office。你可以实现诸如:Word文件打印、传送数据到Word文档、发送E-MAIL、自动产生表格、Excel数据统计、圆饼图,直方图显示、自动报表生成、播放幻灯、doc...
虽然同为C语言,但是在不同操作系统下对线程操作的语句还是差异很大的。这主要是操作系统的不同而引起的不同。对比与Linux下纯C环境下,《【Linux】线程互斥》(点击打开链接),Windows如果仅在...
Unix操作系统紧紧依赖进程创建来满足用户需求
1、进程是资源和分配的基本单位,而进程内核对象就是与进程相关联的一个数据结构。操作系统内核通过它管理进程,即进程控制块(PCB)。
进程一般被定义为一个正在运行的程序的一个实例,它由两部分组成:
线程创建时,系统会分配给线程一些资源,我们可以看到的就是线程描述符,线程堆栈,在系统内部还会有更复杂的系统维护一些信息,在线程创建时,内核总会为其维护一些资源,比较理想的情况是线程运行结束后,释放系统...
本篇系列参考MoreWindows系列秒杀多线程:
他的最新文章
讲师:吴岸城
您举报文章:
举报原因:
原文地址:
原因补充:
(最多只允许输入30个字)

我要回帖

更多关于 线程池执行完毕 的文章

 

随机推荐