// Numbers.c ----- 一个真正的多线程程序
#define WIN32_LEAN_AND_MEAN
#include <stdio.h>
#include <stdlib.h>
#include <windows.h>
// 线程主函数(相当于主线程的main)
// LPVOID A 32-bit pointer to an unspecified type
DWORD WINAPI ThreadFunc(LPVOID);
int main(char argc, char* argv[])
{
HANDLE hThrd;
DWORD threadId;
int i;
for(i=0; i<5; i++)
{
hThrd = CreateThread(NULL,
0,
ThreadFunc,
(LPVOID)i,
0,
&threadId);
if(hThrd)
{
printf("Thread launched %d/n", i);
CloseHandle(hThrd);
}
}
// wait for the threads to commplete
// it's not the best way
Sleep(2000);
return EXIT_SUCCESS;
}
DWORD WINAPI ThreadFunc(LPVOID n)
{
int i;
for(i=0; i<10; i++)
{
printf("%d%d%d%d%d%d%d%d/n",n,n,n,n,n,n,n,n);
}
return 0;
}
/***************************************************************************************
1.多线程程序无法预期
2.执行次序无法保证
3.线程并不总是立刻移动
CreateThread()传回两个值:
threadId: 用于识别线程
HANDLE: 线程句柄
handle又称核心对象(kernel objec)核心对象和所谓GDI对象,如画笔、画刷、DC等是差不多
的。只不过它是由KERNEL32.DLL管理,而不是GID32.DLL管理。
GDI对象是Windows的基础部分。在win16 win32中他们都是由操作系统管理。通常不需要知道
其数据格式。例如,可能会调用SelectObject()或者ReleaseObject()以处理GDI对象;Windows隐藏
了实现细节,只是给你一个HDC或者一个HBRUSH,那都是对象的handle
核心对象以HANDLE为使用时的参考依据,与GDI的HBRUSH、HPEN、HPALETTE以及其他hanles不同
的是,只有一种handle可以代表核心对象,所谓handle,其实是个指针,指向操作系统内存空间中的
某样东西,那些东西不允许你直接取得。你的程序不能够直接取用它,为的是维护系统的完整性与安
全性。
win32核心对象清单
processes 进程
threads 进程
files 文件
events 事件
semaphores 信号量
mutexes 互斥器
Pipes(named 和 anonymous) 管道
GDI对象和核心对象之间有一个主要的不同。
GDI对象有单一拥有者,不是进程就是线程;
核心对象可以有一个以上的拥有者,甚至可以跨进程,为了保持对每一个主人的追踪,核心对象
保持了一个引用计数器(reference counter),以记录有多少handles对应到此对象。对象也记录了
那一个进程或者线程是拥有者。
调用CreateThread()或是其他会传回handle的函数,应用计数器便累加1.当调用CloseHandle()
时,引用计数器递减1.一旦降至0,这一核心对象即自动被销毁。
打开一个对象,区分其拥有者是进程还是线程关系到系统如何清除善后(cleans up)。所谓cleanup
操作,包括将该进程或线程所拥有的每一个对象的引用计数减1.若有必要对象直接被摧毁。
程序员不能选择由进程或者线程拥有对象,一切得视对象类型而定。
CloseHandle() 重要性
BOOL CloseHandle(
HANDLE hObject
);
hObject 代表一个已打开的对象handle
返回值 成功返回TRUE 失败返回 FALSE
调用GetLastError()获取失败原因
如果一个进程没有在结束之前针对它所打开的核心对象调用CloseHandle(),操作系统就会自动把
那些对象的引用计数器下降1.虽然你可以依赖系统做实体生的清除,然而逻辑上的清除工作是完全不
同的事情,特别是如果有许多个进程的话。因为系统不知道对象实际代表什么意义,所以不可能知道
解构顺序是否重要。
如果一个进程常常产生“worker”线程,而老是不关系线程的handle,那个这个进程可能最终
有数百甚至上千个开启的“线程核心对象”留给操作系统去清理,这里就造成了资源泄漏。
×worker线程:完全不牵扯到图形用户界面的线程。
你不可能依赖“因线程的结束而清理所有被这一线程产生的核心对象”。许多对象,如文件,是
被进程拥有的,而非线程拥有。在进程结束前不能够清理他们。
线程对象和线程的不同
线程的handle是指向“线程核心对象”,而不是指向线程本身。对大部分API而言,这项差异没有
什么印象。当你调用CloseHandle()并给予它一个线程handle时,你只不过是表示,你希望自己和核心
对象不再有任何瓜葛。CloseHandle()唯一做的事情就是把引用计数减1.如果该值变成0,对象会自动
被操作系统摧毁。
“线程核心对象”引用到的那个线程也会令核心对象开启。应此,线程对象默认引用计数是2.当
调用CloseHandle()时,引用计数下降1,当线程结束是再降1.只有当两件事都发生了的时候,这个对象
才真正被清除。
“引用计数”机制保证新的线程有个地方可以写下其返回值。这样的机制也保证旧的线程能够读取
那个返回值----只要没有调用CloseHandle()
CreateThread()传回的handle属于进程所有,而非线程所有,所以很可能有一个新产生的线程调用
CloseHandle(),取代原来的线程。
***************************************************************************************/