Win32 线程知识点梳理一

Win32 线程知识点梳理一

为什么不使用多个进程

  • 线程价廉
  • 如果使用多进程,将窗口的handle交给另一个进程是一个困难的问题,因为handle只在其诞生的进程中才有意义,避免某个进程危及另一个进程的资源,在一个多线程程序中,所有的线程都可以使用这个窗口的handle,因为handle和线程在同一个进程之中

  • web服务器中为每个请求产生一个新的进程很容易,但额外负担非常惊人,必须载入服务器软件的一个全新副本,配置大量内存和初始化,并且将状态调整到与原先状态相同。如果请求的数据为8kb,为了这8kb数据操作系统操作几MB内存来为此服务。

    Unix 产生一个进程所需的额外负担(overhead)比在Win32中低得多,然而使用Win32产生一个线程代价又更低廉得多。
    事实上,不需要任何额外开销的方式还是异步方式,它使用非阻塞的方式与每个客户通信,服务器使用一个进程进行轮询就行了。

Win32 线程常用函数

1. 产生一个线程:CreateThread
HANDLE WINAPI CreateThread(
  _In_opt_  LPSECURITY_ATTRIBUTES  lpThreadAttributes,
  _In_      SIZE_T                 dwStackSize,
  _In_      LPTHREAD_START_ROUTINE lpStartAddress,
  _In_opt_  LPVOID                 lpParameter,
  _In_      DWORD                  dwCreationFlags,
  _Out_opt_ LPDWORD                lpThreadId
);

参数1:安全属性,NULL使用缺省值
参数2:新线程拥有自己的堆栈,0表示使用缺省大小为1MB
参数3:新线程起始地址,一个函数指针
参数4:上传到指定的新线程函数去,作为参数
参数5:允许产生一个暂时挂起的线程,默认是立即开始执行
参数6:新线程ID传回这里,线程ID是一个全局变量,可以独一无二的表示系统中任一进程中的某个线程
创建成功,函数返回一个handle代表新线程,否则传回false。

创建5个线程用例:

#include <stdio.h>
#include <stdlib.h>
#include <windows.h>
DWORD WINAPI ThreadFunc(LPVOID n){
    int i ;
    for(int i=0;i<10;++i){
        printf("%d\n",n);
    }
    return 0;
}
int main(){
    HANDLE hThrd;
    DWORD threadId;
    int i ;
    //创建5个线程
    for(int i=0;i<5;i++)
    {
        hThrd = CreateThread(NULL,0,ThreadFunc,(LPVOID)i,0,&threadId);
        if(hThrd){ //返回不为false表示创建成功
            printf("Thread launched %d\n",i);
        }
    }
    Sleep(2000);
    return EXIT_SUCCESS;
}

运行结果:
运行结果

备注:
WINAPI是一种调用约定,在WINDEF.H中定义为_stdcall

关于handle

handle 是一种核心对象,所谓handle其实是一个指针,指向操作系统内存空间中的某项东西,不允许你直接取得,维的是维护操作系统的完整性和安全性。
Win32 核心对象:
- 进程
- 线程
- 文件
- 事件
- 信号量
- 互斥器
- 管道
核心对象保持了一个引用计数,以记录有多少handles对应到此对象,也记录了哪一个进程或线程是拥有者,当引用计数降为0核心对象就会被摧毁。
由于引用计数的设计,如果某个进程拥有某个handle,即使这个handle的创建进程已经结束了,该handle也不会被摧毁。

线程的handle的默认引用计数为2,当调用CloseHandle时引用计数减1,当线程结束时,引用计数再减1,只有这两件事都发生了,这个对象才会被真正清除。

2. 线程结束代码
BOOL WINAPI GetExitCodeThread(
  _In_  HANDLE  hThread,
  _Out_ LPDWORD lpExitCode
);

参数1 : 线程handle
参数2 : 结束代码,如果成功返回true,失败返回false,如果线程尚未结束,将返回STILL_ACTIVE

3. 结束一个线程:ExitThread
VOID WINAPI ExitThread(
  _In_ DWORD dwExitCode
);

参数1 : 线程的结束代码
可以在线程的函数里调用。

主线程的特点
  • 线程必须负责GUI程序中的主消息循环
  • 这个线程结束会使得程序中所有的线程都被强迫结束

Win32 说明文件一再强调线程分为GUI线程(拥有消息队列的线程)和worker线程(如果worker线程也产生了一个窗口,那么就会有一个消息队列被产生出来附着到此线程身上,这时视为GUI线程)两种。
GUI线程负责建造窗口,处理消息主循环,worker线程负责纯粹的运算工作。

4. 等待一个线程的结束:WaitForSingleObject

等待某个东西是线程经常要做的事,比如等待磁盘存取,这时使用busy loop是一个坏点子,它会对系统的效率进行冲击,因为操作系统没有能力分辨哪个线程的工作是有用的,所以每个线程一律获得平等的CPU时间,可能这时等待的线程所有得工作就是在检查GetExitCodeThread()的传回值。
那么我们可以怎么做?
我们可以让这个等待的线程暂时罢工,需要一个新版的sleep(),它能够在某个线程结束时被调用。
Win32提供的函数名为WaitForSingleObject()

DWORD WINAPI WaitForSingleObject(
  _In_ HANDLE hHandle,
  _In_ DWORD  dwMilliseconds
);

参数1 : 等待对象的handle
参数2:等待的最长时间,0表示立即返回,INFINITE表示无穷等待
返回值:
WAIT_FAILED ,函数失败。
成功时返回有三种状态:
等待的handle变为激发状态,这种情况下返回值为WAIT_OBJECT_0
核心态变为激发状态之前等待时间结束,返回WAIT_TIMEOUT
拥有mutex的线程结束前没有释放mutex,传回WAIT_ABANDONED

可被WaitForSingleObject()使用

5. 在同一时间等待一个以上的对象
DWORD WINAPI WaitForMultipleObjects(
  _In_       DWORD  nCount,
  _In_ const HANDLE *lpHandles,
  _In_       BOOL   bWaitAll,
  _In_       DWORD  dwMilliseconds
);

参数1:lpHandles所指的handle数组中元素个数,最大容量是MAXIMUM_WAIT_OBJECTS
参数2:指向一个由对象handles所组成的数组,这些handles不需要为相同的类型
参数3:如果为true,表示所有的handles都必须激发,函数才得以返回
参数4: 指定时间长度,当时间结束及时没有任何handle激发也会返回。INFINITE表示无穷等待
返回值:
时间终了:WAIT_TIMEOUT
如果bWaitAll为true,返回值为WAIT_OBJECT_0
如果为false,返回值-WAIT_OBJECT_0表示数组中哪一个handle被激发了
如果等待的对象中有任何的mutexes,返回值可能为
WAIT_ABONDONED_0 ~ WAIT_ABANDONED_0+nCount-1
函数失败,返回WAIT_FAILED
这部分的代码(按照书上的内容手动输入测试了一遍)

#include <stdio.h>
#include <stdlib.h>
#include <windows.h>

DWORD WINAPI ThreadFunc(LPVOID n){
    srand(GetTickCount());
    Sleep((rand())%10*800+500);
    printf("slot %d idle\n",n);
    return((DWORD)n);
}
#define THREAD_POOL_SIZE 3
#define NUM_TASKS 6
int main(){
    HANDLE hthrd[THREAD_POOL_SIZE];
    int slot =0;
    DWORD threadId;
    int i;
    DWORD rc;
    for(i=1;i<=NUM_TASKS;i++){
        if(i>THREAD_POOL_SIZE){
            //等待一个线程结束
            rc = WaitForMultipleObjects(THREAD_POOL_SIZE,hthrd,FALSE,INFINITE); 
            slot = rc - WAIT_OBJECT_0;
            printf("slot %d terminated\n",slot);
            CloseHandle(hthrd[slot]);
        }
        hthrd[slot++]=CreateThread(NULL,0,ThreadFunc,(LPVOID)slot,0,&threadId);
        printf("launched thread %d (slot %d)\n",i,slot);
    }
    //现在等待所有的线程结束
    rc = WaitForMultipleObjects(THREAD_POOL_SIZE,hthrd,TRUE,INFINITE);
    for(slot =0;slot<THREAD_POOL_SIZE;slot++)
        CloseHandle(hthrd[slot]);
    printf("All slots terminated..\n");
    return EXIT_SUCCESS;
}

result


资料来源

Win32多线程程序设计

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值