Windows线程

简介: Windows线程
Windows线程
线程基础
  • Windows线程是可以执行的代码的实例。
    系统是以线程为单位调度程序,一个程序中可以有多个线程,实现多任务的处理。主线程只能有一个。
  • Windows线程的特点
  1. 线程都具有1个ID
  2. 每个线程都具有自己的内存栈,其余的共享。
  3. 同一进程中的线程使用同一个地址空间。(除了栈空间)
  • 线程的调度
    操作系统将CPU的执行时间划分为时间片,依次根据时间片执行不同的线程。
    线程轮询: 线程A -> 线程B ->线程A …
创建线程
  • 创建线程
HANDLE CreateThread(
  LPSECURITY_ATTRIBUTES lpThreadAttributes , // 安全属性,已废弃, NULL
    SIZE_T dwStackSize, // 线程栈的大小  按1M对齐,最少1M
    LPTHREAD_START_ROUTINE lpStartAddress, // 线程处理函数的函数地址,自己定义由系统调用
    LPVOID lpParameter, // 传递给线程处理函数的参数
    DWORD dwCreationFlags, // 线程的创建方式(立即执行&挂起方式)
    // 0 -> 立即启动
    // CREATE_SUSPENDED -> 挂起
    LPDWORD lpThreadId // 创建成功,返回线程的ID
); // 创建成功,返回线程句柄  ( 线程句柄和线程ID都可以代表线程 )
  • 定义线程处理函数
DWORD WINAPI ThreadProc(
  LPVOID lpParameter // 创建线程时,传递给线程的参数
);

Demo

#include <windows.h>
#include <stdio.h>
DWORD CALLBACK TestProc(LPVOID pParam){
   char * pszText = (char *) pParam;
   printf("%s",pszText);
}
int main(){
    DWORD nID = 0;
    char * pszText = "******";
    HANDLE hThread = CreateThread(NULL,0,TestProc,pszText,0,&nID);
  getchar();
  return 0;
}

子线程正常执行->要保证主线程不结束

销毁线程
  • 挂起
DWORD SuspendThread(
  HANDLE hThread // 线程句柄
);
  • 唤醒
DWORD ResumeThread(
  HANDLE hThread  // 线程句柄
);
  • 结束指定线程
BOOL TerminateThread(
  HANDLE hThread, // 线程句柄
    DWORD dwExitCode // 退出码,一般没用
);
  • 结束函数所在的线程
VOID ExitThread(
  DWORD dwExitCode // 退出码,没有实际意义
);
// 自杀 -> 只能干掉调用的线程
线程相关操作
  • 获取当前线程ID
GetCurrentThreadId();
  • 获取当前线程的句柄
GetCurrentThread();
  • 等候单个句柄有信号
VOID WaitForSingleObject(
  HANDLE handle , // 句柄BUFF的地址
    DWORD dwMilliseconds // 最长等候时间 ms
    // INFINITE 一直等待
);
// 有信号时会立即返回
// 没有信号会阻塞

线程句柄是可等候句柄: 有信号和无信号状态

  • 同时等候多个句柄有信号
DWORD WaitForMultipleObject(
  DWORD nCount, // 句柄数量
    CONST HANDLE *lpHandles, // 句柄BUFF的地址
    BOOL bWaitAll, // 等候方式
    // TRUE : 所有句柄都有信号才会返回
    // FALSE: 只要有一个有信号就返回
    DWORD dwMilliseconds // 等候时间  INFINITE
);

可等候句柄

  • 线程信号
  • 当线程处于执行状态时,线程无信号
  • 当线程结束那刻,线程有信号
线程同步
原子锁
  • 当多个线程对同一个数据进行原子操作,会产生结果丢失,比如算术运算
  • 使用原子锁函数
InterlockedIncrement   // ++ 操作符
InterlockedDecrement   // --
InterlockedCompareExchange   // 三目运算符 
InterlockedExchange    // = 赋值
  • 原子锁的实现,直接对数据所在的内存操作,并且任何一个瞬间只能有一个线程访问。
  • 只能对运算符加锁,效率高,但是麻烦
互斥锁
  • 相关问题
    多线程下代码或者资源的共享使用
  • 互斥的使用
  1. 创建互斥
HANDLE CreateMutex(
  LPSECURITY_ATTRIBUTES lpMutexAttributes, // 安全数据(NULL)
    BOOL bInitialOwner, // 初始的拥有者 TRUE/FALSE
    // TRUE: 哪个线程创建哪个线程拥有
    // FALSE: 都不拥有
    LPCTSTR lpName // 命名
); // 创建成功返回互斥句柄
// 互斥句柄,也是可等候句柄
  • 在任何时间点上只有一个线程拥有互斥, 独占性和排他性
  • 当任何线程都不拥有互斥,互斥有信号,任何一个线程拥有互斥,互斥就没有信号
  1. 等候互斥
    WaitFor… 函数
    互斥的等候遵循谁先等候谁先获得

也就是加锁

  1. 释放互斥
BOOL ReleaseMutex(
  HANDLE hMutex // 互斥锁句柄
);
  1. 关闭互斥句柄
CloseHandle

demo

#include <windows.h>
#include <stdio.h>
HANDLE hmutex; // 接收互斥句柄 
DWORD CALLBACK TestProc(LPVOID pParam){
   char * pszText = (char *) pParam;
   int i ;
   while(1){
        WaitForSingleObject(hmutex,INFINITE);
      for(i = 0;i<strlen(pszText);i++){
        printf("%c",pszText[i]);
        Sleep(125);
     }
     printf("\n");
     ReleaseMutex(hmutex); 
   } 
}
int main(){
  hmutex = CreateMutex(NULL,FALSE,NULL); 
    DWORD nID1 = 0;
    DWORD nID2 = 0;
    char * pszText1 = "******";
    char * pszText2 = "------";
    HANDLE hThread1 = CreateThread(NULL,0,TestProc,pszText1,0,&nID1);
    HANDLE hThread2 = CreateThread(NULL,0,TestProc,pszText2,0,&nID2);
  getchar();
  CloseHandle(hmutex);
  return 0;
}
事件
  • 相关问题
    线程之间的通知问题
  • 事件的使用
  1. 创建事件
HANDLE CreateEvent(
  LPSEURITY_ATTRIBUTES lpEventAttributes, // 安全属性
    BOOL bManualReset, // 事件重置(复位)方式 有信号 -> 无信号
    // TRUE  手动
    // FALSE 自动 --> 读取信号一次就会自动复位
    // 触发 无信号 ->有信号
    BOOL bInitialState ,// 事件初始状态,TRUE 有信号
    LPCTSTR lpName // 事件命名,可以为空
);// 创建成功返回事件句柄

可等候句柄

事件的有信号无信号可控制

  1. 等候事件
WaitForSingleObject  / WaitForMultipleObjects
// 自动复位方式,会自动复位
  1. 触发事件( 将事件设置为有信号状态)
BOOL SetEvent(
  HANDLE hEvent //handle to event
);
  1. 复位事件( 将事件设置为无信号状态 )
BOOL ResetEvent(
  HANDLE hEvent
);
  1. 关闭事件
CloseHandle

要小心事件的死锁问题

demo

#include <windows.h>
#include <stdio.h>
HANDLE g_hEvent = 0; // 接收事件句柄
DWORD CALLBACK PrintProc(LPVOID pParam){
  while(1){
    WaitForSingleObject(g_hEvent,INFINITE); // 等待信号 
    ResetEvent(g_hEvent); 
    printf("......\n");
  }
}
DWORD CALLBACK CtrlProc(LPVOID pParam){
  while(1){
    Sleep(1000);
    SetEvent(g_hEvent);
  }
} 
int main(){
  g_hEvent = CreateEvent(NULL,TRUE,FALSE,NULL);
  DWORD nID = 0;
  HANDLE hThread[2] = {0};
  hThread[0] = CreateThread(NULL,0,PrintProc,NULL,0,&nID); 
  hThread[1] = CreateThread(NULL,0,CtrlProc,NULL,0,&nID);
  WaitForMultipleObjects(2,hThread,TRUE,INFINITE);
  CloseHandle(g_hEvent);
  return 0; 
}
信号量
  • 相关的问题
    作用类似于事件,解决通知的相关问题。
    提供一个计数器,可以设置次数
  • 信号量的使用
  1. 创建信号量
HANDLE CreateSemaphore(
  LPSECTRITY_ATTRIBUTES lpSemaphoreAttributes, // 安全属性
    LONG lInitialCount, // 初始化信号量数量
    // 信号量计数值为0时,没有信号
    // 信号量计数值不为0,有信号
    LONG lMaximumCount, // 信号量最大个数
    LPCTSTR lpName // 命名
);// 创建成功返回信号量句柄

可等候句柄

  1. 等候信号量
WaitFor ...
// 每等候通过一次,信号量的信号减1,直到为0阻塞
  1. 给信号量指定计数值
BOOL ReleaseSemaphore(
  HANDLE hSemaphore, // 信号量句柄
    LONG lReleaseCount ,// 释放数量
    LPLONG lpPreviousCount 
    // 返回的信息,返回释放前原来信号量的数量,可以为NULL
);
  1. 关闭句柄
CloseHandle

demo

#include <windows.h>
#include <stdio.h>
HANDLE g_hSema = 0;// 保存信号量句柄
DWORD CALLBACK TestProc(LPVOID pParam){
  while(1){
    WaitForSingleObject(g_hSema,INFINITE);
    printf("*****\n");
  }
}
 
int main(){
  g_hSema = CreateSemaphore(NULL,3,10,NULL);
  DWORD nID = 0;
  HANDLE hThread = CreateThread(NULL,0,TestProc,NULL,0,&nID);
  getchar();
  ReleaseSemaphore(g_hSema,5,NULL);
  WaitForSingleObject(hThread,INFINITE);
  CloseHandle(g_hSema); 
  return 0;
}
相关文章
|
4月前
|
Java Windows
【Azure Developer】Windows中通过pslist命令查看到Java进程和线程信息,但为什么和代码中打印出来的进程号不一致呢?
【Azure Developer】Windows中通过pslist命令查看到Java进程和线程信息,但为什么和代码中打印出来的进程号不一致呢?
|
6月前
|
安全 API C++
逆向学习Windows篇:C++中多线程的使用和回调函数的实现
逆向学习Windows篇:C++中多线程的使用和回调函数的实现
214 0
|
Java 程序员 Windows
[笔记]Windows核心编程《十一》Windows线程池
[笔记]Windows核心编程《十一》Windows线程池
36707 4
[笔记]Windows核心编程《十一》Windows线程池
|
缓存 编译器 调度
[笔记]Windows核心编程《七》用户模式下的线程同步
[笔记]Windows核心编程《七》用户模式下的线程同步
|
编译器 索引 Windows
[笔记]Windows核心编程《二十一》线程本地存储器TLS
[笔记]Windows核心编程《二十一》线程本地存储器TLS
119 0
|
存储 编译器 C++
[笔记]Windows核心编程《十六》线程栈
[笔记]Windows核心编程《十六》线程栈
104 0
|
存储 API 调度
[笔记]Windows核心编程《八》用内核对象进行线程同步
[笔记]Windows核心编程《八》用内核对象进行线程同步
|
算法 安全 调度
[笔记]Windows核心编程《六》线程调度、优先级和关联性
[笔记]Windows核心编程《六》线程调度、优先级和关联性
147 0
|
存储 安全 API
[笔记]Windows核心编程《五》线程基础
[笔记]Windows核心编程《五》线程基础
101 0
|
Windows
windows下创建线程编程
windows下创建线程编程
66 0