windows基础编程 – 进程和线程

  • Post author:
  • Post category:其他


前言

 微软鼓励大家多用线程,而弱化子进程的使用,所以和unix不同,windows下进程的通信很少,内存映射文件是一种。

进程

  • 进程环境信息(进程上下文)

    每个进程启动时系统都会生成相关的环境表,获取里面的环境变量可使用下面函数

    GetEnvironmentStrings FreeEnvironmentStrings 可以获得该环境表的首地址

    SetEnvironmentVariable GetEnvironmentVariable 可以设置和得到环境变量的值

  • 获取进程ID和进程句柄

    GetCurrentProcessId 获取id

    GetCurrentProcess 获得句柄,但是一个假句柄,值为-1,可正常使用

    OpenProcess 通过id获得进程句柄,通常拿来获得别的进程的句柄,也可调用OpenProcess(GetCurrentProcessId)获得本进程真实句柄

  • 创建和结束进程

    BOOL CreateProcess(

    LPCTSTR lpApplicationName,应用程序全路径名

    LPTSTR lpCommandLine, // 命令行参数

    LPSECURITY_ATTRIBUTES lpProcessAttributes, // 安全属性 设为NULL

    LPSECURITY_ATTRIBUTES lpThreadAttributes, // 线程安全属性NULL

    BOOL bInheritHandles, // 继承标识 NULL

    DWORD dwCreationFlags, // 创建方式 给0立即启动

    LPVOID lpEnvironment, // 设置环境信息 可为NULL

    LPCTSTR lpCurrentDirectory, // 设置工作目录 可为NULL

    LPSTARTUPINFO lpStartupInfo, // 保存起始信息 需放入结构体地址

    LPPROCESS_INFORMATION lpProcessInformation // 创建的进程相关信息,需放入结构体地址

    );

    ExitProcess 可以结束当前进程

    TerminateProcess 可以结束别的进程

    CloseHandle 将句柄置为 -1,进程不结束

    • 进程间的等候,即阻塞函数等待别的进程返回

      DWORD WaitForSingleObject(

      HANDLE hHandle, // 进程句柄

      DWORD dwMilliseconds // 等待时间(INFINITE为无限长)

      );

      DWORD WaitForMultipleObjects(

      DWORD nCount, // 句柄数量

      CONST HANDLE *lpHandles, // 存放句柄的数组

      BOOL fWaitAll, // 等候方式TRUE等待所有 FALSE任何一个

      DWORD dwMilliseconds // 等待时间(INFINITE为无限长)

      );

      这两个等候的函数一个可以等待单个进程,一个可以等候多个,里头的HANDLE其实可以不光只是进程,也可以是线程,只要是带信号的句柄,这些句柄包括

      Change notification

      Console input 控制台输入

      Event 事件

      Job

      Mutex 互斥

      Process 进程

      Semaphore 信号

      Thread 线程

      Waitable timer 可等候定时器

线程

进程如果对应电脑的内存,线程对应着电脑的CPU,线程也就是执行的代码的实例。
系统是以线程为单位调度程序。
线程的调度: 将CPU的执行时间划分成时间片,依次根据时间片执行不同的线程。

– 创建

HANDLE CreateThread(

LPSECURITY_ATTRIBUTES lpThreadAttributes, //安全属性

DWORD dwStackSize, // 线程栈大小为0向1M靠拢

LPTHREAD_START_ROUTINE lpStartAddress, // 线程处理函数

LPVOID lpParameter, // 处理函数参数

DWORD dwCreationFlags, //创建方式 挂起还是执行

LPDWORD lpThreadId // 接收的线程ID

);

– 结束 TerminateThread / ExitThread

结束后依旧需要调用CloseHandle释放句柄资源

– 挂起/执行 SuspendThread ResumeThread

– 线程信息 GetCurrentThreadId / GetCurrentThread

线程同步

线程同步技术是解决线程之间的资源竞争和线程之间的协调工作。

其中,原子锁、临界区(段)、互斥 是给线程间加锁,

  • 原子锁

    直接对一个数据的内存操作,任一时间只有一个线程访问该内存,可对这个数据进行增,减,改变数值

    InterlockedIncrement, InterlockedExchange, InterlockedDecrement
  • 临界区

    可以锁定一段代码,防止多个线程同时使用该段代码

    VOID InitializeCriticalSection(

    LPCRITICAL_SECTION lpCriticalSection // 临界区结构体的地址

    ); //初始化一个临界区

    VOID EnterCriticalSection(

    LPCRITICAL_SECTION lpCriticalSection

    ); //进入临界区,放在要锁定的代码前

    VOID LeaveCriticalSection(

    LPCRITICAL_SECTION lpCriticalSection //

    ); //离开临界区,放在锁定的代码结尾

    VOID DeleteCriticalSection(

    LPCRITICAL_SECTION lpCriticalSection

    ); //释放临界区资源

  • 互斥

    用于多线程下代码或资源的共享使用

    HANDLE CreateMutex(

    LPSECURITY_ATTRIBUTES lpMutexAttributes,

    // 安全属性

    BOOL bInitialOwner, // 初始拥有着,TRUE立刻获得互斥锁

    LPCTSTR lpName // 互斥名

    ); //返回互斥句柄

    一个进程调用WaitForSingleObject时,如果持锁,则无需等待,否则等待持锁线程调用ReleaseMutext时立刻获得互斥锁

    CloseHandle 可以释放互斥锁句柄资源,OpenMutex可以根据互斥名获得互斥锁句柄,也可以使用在两个进程间的通信。

    注: 互斥和临界区的区别,

    临界区 – 运行在用户态,执行效率高,只能在同一个进程中使用

    互斥 – 运行在内核态,执行效率低,可以通过命名方式跨进程使用(配合内存映射文件)

  • 事件

    事件用于程序之间的通知的问题,是最常用的线程同步技术,事件句柄也分为有信号和无信号两种状态,无信号时会被WaitFor..函数等待,而SetEvent和ResetEvent可以手动设置事件句柄的信号状态

    HANDLE CreateEvent(

    LPSECURITY_ATTRIBUTES lpEventAttributes,

    // 安全属性

    BOOL bManualReset, // 手动还是自动复位

    BOOL bInitialState, // 初始状态(有无信号)

    LPCTSTR lpName // 事件名字

    );

    另一进程调用WaitFor..函数等待时间句柄变为有信号状态,如果是自动复位,会在调用结束后信号立马变成无信号状态,如果是手动,需要在调用结束后使用ResetEvent将信号置为无信号状态

    不使用以后使用CloseHandle关闭事件句柄

  • 信号量

    类似于事件,解决通知的相关问题,可以提供一个计数器,设置信号有效的次数

    HANDLE CreateSemaphore(

    LPSECURITY_ATTRIBUTES lpSemaphoreAttributes,

    // 安全属性

    LONG lInitialCount, // 初始计数值

    LONG lMaximumCount, // 最大值

    LPCTSTR lpName // 信号量名字

    );

    当一个线程调用WaitFor..函数会使信号量计数值减1,直到该值为0这个信号量会变为无信号状态。使用ReleaseSemaphore可以重置信号量的计数值。使用结束后依旧使用CloseHandle释放资源



版权声明:本文为diletrich原创文章,遵循 CC 4.0 BY-SA 版权协议,转载请附上原文出处链接和本声明。