win32 多线程 1

来源:互联网 发布:淘宝大学总裁班有用吗 编辑:程序博客网 时间:2024/06/06 01:15

// 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(),取代原来的线程。
***************************************************************************************/