Windows提高_2.1第一部分:线程

第一部分:线程

什么是线程?

  • 线程其实可以理解为一段正在执行中的代码,它最少由一个线程内核对象和一个栈组成。
  • 线程之间是没有从属关系的,同一进程下的所有线程都可以访问进程内的所有内容。
  • 主线程其实是创建进程时创建的线程,主线程一旦退出,所有子线程也会退出。

创建一个线程

#include <stdio.h>
#include <process.h>
#include <windows.h>
?
// 线程函数
DWORD WINAPI WorkerThread(LPVOID lpThreadParameter)
{
    while (true)
    {
        printf("WorkerThread()\n");
    }
}
?
int main()
{
    DWORD ThreadId = 0;

    // 如何创建一个线程
    HANDLE Thread = CreateThread(
        NULL,               // 安全属性
        0,                  // 设置栈的大小,使用默认
        WorkerThread,       // 表示的是线程的开始位置
        NULL,               // 线程函数的参数
        NULL,               // 创建标志
        &ThreadId);         // 创建出的线程的 Id
?
    // 可以使用 process.h 提供的函数更加安全的创建和结束线程
    // _beginthreadex() + _endthreadex()

    while (true)
    {
        printf("main()\n");
    }
?
    return 0;
}

代码 - 等待线程

#include <stdio.h>
#include <windows.h>
?
// 线程函数
DWORD WINAPI WorkerThread(LPVOID lpThreadParameter)
{
    // 获取传入的参数
    int count = (int)lpThreadParameter;
?
    // 循环次数 100
    for (int i = 0; i < count; ++i)
        printf("i = %d\n", i);
?
    return 0;
}
?
int main()
{
    // 如何创建一个线程
    HANDLE Thread = CreateThread(
        NULL,               // 安全属性
        0,                  // 设置栈的大小,使用默认
        WorkerThread,       // 表示的是线程的开始位置
        (LPVOID)500,        // 线程函数的参数
        NULL,               // 创建标志
        NULL);              // 创建出的线程的 Id
?
    // 线程内核对象的信号:
    // - 有信号: 当线程运行结束的时候,处于有信号状态
    // - 无信号: 当线程正在执行的时候,处于无信号状态
?
    // 等待线程知道线程退出为止
    WaitForSingleObject(Thread, INFINITE);
?
    // 主线程一旦退出,子线程也会退出
?
    return 0;
}

代码 - 遍历线程

#include <stdio.h>
#include <windows.h>
// 1. 包含头文件
#include <TlHelp32.h>
?
int main()
{
    int Pid = 0;
    scanf_s("%d", &Pid);
?
    // 2. 拍摄当前所有线程的快照,参数 2 是无效的,传入任何值遍历的都是所有的线程
    HANDLE Snapshot = CreateToolhelp32Snapshot(TH32CS_SNAPTHREAD, 0);
?
    // 3. 检查快照是否创建成功
    if (Snapshot == INVALID_HANDLE_VALUE)
    {
        MessageBox(NULL, L"快照创建失败", L"标题", MB_OK);
        ExitThread(-1);
    }
?
    // 4. 创建结构体用于保存遍历到的信息
    THREADENTRY32 ThreadInfo = { sizeof(THREADENTRY32) };
?
    // 5. 尝试遍历到第一个线程信息
    if (Thread32First(Snapshot, &ThreadInfo))
    {
        do {
            // [ 判断遍历到的线程所属进程 id 是否为想要遍历的进程 ]
            if (Pid == ThreadInfo.th32OwnerProcessID)
            {
                printf("tid: %d\n", ThreadInfo.th32ThreadID);
            }
        } while (Thread32Next(Snapshot, &ThreadInfo));
    }
?
    return 0;
}

代码 - 挂起和恢复

#include <stdio.h>
#include <windows.h>
// 1. 包含头文件
#include <TlHelp32.h>
?
int main()
{
    int Pid = 0;
    scanf_s("%d", &Pid);
?
    // 2. 拍摄当前所有线程的快照,参数 2 是无效的,传入任何值遍历的都是所有的线程
    HANDLE Snapshot = CreateToolhelp32Snapshot(TH32CS_SNAPTHREAD, 0);
?
    // 3. 检查快照是否创建成功
    if (Snapshot == INVALID_HANDLE_VALUE)
    {
        MessageBox(NULL, L"快照创建失败", L"标题", MB_OK);
        ExitThread(-1);
    }
?
    // 4. 创建结构体用于保存遍历到的信息
    THREADENTRY32 ThreadInfo = { sizeof(THREADENTRY32) };
?
    // 5. 尝试遍历到第一个线程信息
    if (Thread32First(Snapshot, &ThreadInfo))
    {
        do {
            // [ 判断遍历到的线程所属进程 id 是否为想要遍历的进程 ]
            if (Pid == ThreadInfo.th32OwnerProcessID)
            {
                printf("tid: %d\n", ThreadInfo.th32ThreadID);
?
                // 打开目标线程的句柄
                HANDLE Thread = OpenThread(THREAD_ALL_ACCESS, FALSE, ThreadInfo.th32ThreadID);
?
                // 尝试进行挂起, 每调用一次就挂起一次
                // SuspendThread(Thread);
?
                // 尝试进行恢复,每调用一次就恢复一次
                // ResumeThread(Thread);
?
                // 当挂起计数为 0 的时候,线程就会被调度
?
                // 用于结束标目线程
                // TerminateThread(Thread, 0);
            }
        } while (Thread32Next(Snapshot, &ThreadInfo));
    }
?
    return 0;
}

代码 - 伪句柄产生的问题

#include <stdio.h>
#include <windows.h>
?
// 使用伪句柄作为参数传递可能会带来的问题
?
// 功能函数,通过传入的线程句柄,获取到线程的创建时间
VOID GetThreadCreateTime(HANDLE Thread)
{
    // 0. 创建用于保存线程相关时间的结构
    FILETIME CreateTime = { 0 }, ExitTime = { 0 };
    FILETIME KernelTime = { 0 }, UserTime = { 0 };
?
    // 1. 使用 GetThreadTimes 获取到传入的线程的相关时间
    GetThreadTimes(Thread, &CreateTime,
        &ExitTime, &KernelTime, &UserTime);
?
    // 2. 将时间转换为本地时间
    FILETIME LocalCreateTime = { 0 };
    FileTimeToLocalFileTime(&CreateTime, &LocalCreateTime);
?
    // 3. 将时间戳转换为系统时间
    SYSTEMTIME SystemTime = { 0 };
    FileTimeToSystemTime(&LocalCreateTime, &SystemTime);
?
    // 4. 输出时间
    printf("CreateTime: %d 时 %d 分 %d 秒\n", SystemTime.wHour,
        SystemTime.wMinute, SystemTime.wSecond);
}
?
// 线程函数
DWORD WINAPI WorkerThread(LPVOID lpThreadParameter)
{
    // 接收传入到线程内的伪句柄
    HANDLE Thread = (HANDLE)lpThreadParameter;
?
    // 根据[伪句柄]输出线程的创建时间
    // [输出的实际上是自己的创建时间]
    GetThreadCreateTime(Thread);
?
    return 0;
}
?
int main()
{
    // 获取当前线程的[伪]句柄
    HANDLE Thread = GetCurrentThread();
?
    // 1. 查看当前[主]线程的创建时间
    GetThreadCreateTime(Thread);
?
    // 2. 等待 2 秒钟,不能保证
    Sleep(2000);
?
    // 3. 创建一个新的线程,将伪句柄传入
    HANDLE Handle = CreateThread(NULL, 0, WorkerThread,
        (LPVOID)Thread, NULL, NULL);
?
    // 4. 等待线程执行完毕
    WaitForSingleObject(Handle, INFINITE);
?
    return 0;
}

总结:由于传入的句柄是一个伪句柄,始终指向当前的线程内核对象,所以导致在工作线程内计算出的时间不是主线程的运行时间。线程伪句柄的值始终为【-2】,进程伪句柄的值始终为【-1】

代码 - 真实句柄的获取

// 将伪句柄转换成真实的句柄
    DuplicateHandle(
        GetCurrentProcess(),    // 从哪里拷贝
        GetCurrentThread(),     // 要拷贝什么
        GetCurrentProcess(),    // 拷贝到哪里去
        &Thread,                // 保存拷贝到的句柄
        0,                      // 安全访问级别
        false,                  // 是否可以被子进程继承
        DUPLICATE_SAME_ACCESS); // 转换选项
?

线程的退出方式

  1. 主线程函数(main\WinMain)返回,最为友好,会调用析构函数、会清理栈
  2. 使用ExitThread:不会调用析构函数
  3. 使用TerminateThread:不会调用析构函数,不会清理栈
  4. 结束进程:可能来不及保存工作结果

线程的优先级

  • 线程只有相对于进程的优先级,随着进程优先级的改变,线程的相对优先级并不会改变
  • 通常情况下手动修改优先级并不会对程序的执行产生变化

原文地址:https://www.cnblogs.com/ltyandy/p/10938171.html

时间: 2024-10-12 21:28:29

Windows提高_2.1第一部分:线程的相关文章

Windows提高_2.2第二部分:用户区同步

第二部分:用户区同步 同步和互斥 同步:就是按照一定的顺序执行不同的线程 互斥:当一个线程访问某一资源的时候,其它线程不能同时访问 多线程产生的问题 #include <stdio.h> #include <windows.h> ? // 全局变量,被不同的线程访问和修改 int g_Number = 0; ? DWORD WINAPI ThreadPro1(LPVOID lpThreadParameter) { // 为 g_Number 自增 100000 次 for (int

Windows提高_2.3第三部分:内核区同步

第三部分:内核区同步 等待函数(WaitForObject) 等待函数的形式 单个:WaitForSingleObject 多个:WaitForMultipleObjects 一个可以被等待的对象通常由两种状态,分别是: 可等待(激发态)(有信号):等待函数[不会阻塞] 不可等待(非激发态)(无信号):等待函数需要等待一定时长并[阻塞] 等待函数的副作用: 改变被等待对象的信号状态 #include <iostream> #include <windows.h> ? // 工作线程

【windows核心编程】使用远程线程注入DLL

前言 该技术是指通过在[目标进程]中创建一个[远程线程]来达到注入的目的. 创建的[远程线程]函数为LoadLibrary, 线程函数的参数为DLL名字, 想要做的工作在DLL中编写.  示意图如下:  相关API 1.创建远程线程 //该函数除了第一个参数为目标进程句柄外 //其他参数均和CreateThread一样 HANDLE hThread = CreateRemoteThread( __in HANDLE hProcess, //目标进程句柄 __in_opt LPSECURITY_A

在Windows系统上实现轻量级的线程间及进程间消息队列

看IaaS 资料时,捎带研究下硬件虚拟化,主要参考<基于intel VT-x 的Xen 全虚拟化实现>,<intel 开发手册 第三卷 19/20章> Intel VT 是intel X86架构的CPU硬件虚拟化技术,新增两种模式: VM root: 即虚拟机管理系统运行模式: VM non root:即虚拟机运行模式: 如下图: VMXON.VMXOFF用以实现打开或关闭虚拟化功能: VM Exit和VM Entry 用以实现non root和root之间的切换:这种转换被VMC

PHP在Windows下安装配置第一步

第一步就是下载和安装PHP解释器了: 1.下载Windows版本的PHP解释器,下载地址:官方下载 我下载的是 VC11 x64 Thread Safe 这个下面的zip包 2.下载完成后,解压到 C:\php 目录,然后打开目录,解压目录下有个ext子目录,保存了php的一些扩展dll 还有2个文件 ,一个 php.ini-development ,一个是 php.ini-production ,从名字也可以看出是开发环境跟生产环境的区别了 3.复制一份php.ini-development文

第一章线程管理

Java 7 并发编程实战手册目录 代码下载(https://github.com/Wang-Jun-Chao/java-concurrency) 第一章线程管理 1.1简介 在计算机领域中,我们说的并发(Concurrency)是指一系列任务的同时运行.如果一 台电脑有多个处理器或者有一个多核处理器,这个同时性(Simultaneity)是真正意义的并 发:但是一台电脑只有一个单核处理器,这个同时性并不是真正的并发 与并发相关的另一个概念是并行(Parallelism).与并发有不同的定义一样

linux程序设计——第一个线程程序(第十二章)

第12章    POSIX线程 在第11章中,介绍了如何在linux中处理进程.但有时人们认为,用fork调用来创建新进程的代价太高.在这种情况下,如果能让一个进程同时做两件事情或者至少看起来是这样会非常有用,而且,人们页希望能有两件或更多的事情以一种非常紧密的方式同时发生,这就需要线程发挥作用了. 12.1    什么是线程 在一个程序中多个执行路线就叫做线程(thread).更准确的定义是:线程是一个进程内部的一个控制序列.虽然linux和许多其他的操作系统一样,都擅长同时运行多个进程,但迄

不管服不服 Windows仍是全球第一大桌面系统

不管服不服 Windows仍是全球第一大桌面系统 近日,根据来自市场调研机构 Net Applications 公布的统计数据显示,Windows 依然是世界上排名第一的操作系统,而且未来将很难被打破,因为现在无论是 Mac OS X 还是 Linux 操作系统的市场份额,均不足以对微软的 Windows 平台产生威胁. 具体而言,截止 2015 年 10 月份,Windows 市场份额仍然高达 90.42%,而 Mac OS X 的份额为 8.00%,Linux 仅有 1.57%. 之前一直有

windows 下获取当前进程的线程数量

#include <TlHelp32.h> int get_thread_amount() { int i = 0; char Buff[9]; PROCESSENTRY32 pe32; pe32.dwSize = sizeof(pe32); int processid = GetCurrentProcessId(); HANDLE hProcessSnap = ::CreateToolhelp32Snapshot(TH32CS_SNAPPROCESS, 0); if (hProcessSna