【Windows线程开发】线程基础

news2025/1/10 20:50:47

本篇文章来带领大家了解Windows线程,了解线程的基本概念,了解线程的创建方式,以及一些简单的线程操作。

文章目录

  • 一.线程基本概念
  • 二.创建线程
  • 三.线程实例(单线程,多线程)
    • 单线程执行
    • 多线程执行
  • 四.挂起,销毁线程
  • 五.线程相关操作

一.线程基本概念

Windows线程是可以执行的代码的实例,系统是以线程为单位调度程序。一个程序中可以有多个线程,实现多任务的处理。

  • Windows线程的特点:
    1. 每个线程都具有一个ID
    2. 每个线程都具有自己的内存栈
    3. 同一进程中的线程使用同一个地址空间
  • 线程的调度:
    将CPU的执行时间划分成时间片,依次根据时间片执行不同的线程
    线程轮询:线程A->线程B->线程A…

二.创建线程

  • 创建线程:
    CreateThread函数:
    MSDN官方文档解释CreateTread函数
    函数功能:创建在调用进程的虚拟地址空间内执行的线程。
    语法:
HANDLE CreateThread(
	LPSECURITY_ATTRIBUTES lpThreadAttributes,//指向SECURITY_ATTRIBUTES结构的指针,该结构确定返回的句柄是否可以由子进程继承
	SIZE_T dwStackSize,//堆栈的初始化大小(以字节为单位)
	LPTHREAD_START_ROUTINE lpStartAddress,//指向由进程执行的应用程序定义函数的指针
	LPVOID lpParmeter,//指向要传递给线程的变脸指针
	DWORD dwCreationFlags,//控制线程创建的标志
	LPDWORD lpThreadID //指向接收线程标识(ID)的变量指针
);

参数说明:
上述参数为微软官方文档中的说法,可能有些不理解,这里我用通俗的语言来向大家解释:
lpThreadAttributes::安全属性
dwStackSize:线程栈的大小,这里的线程栈的大小是以1兆对齐的
lpStartAddress:线程处理函数的函数地址
lpParameter:传递给线程处理函数的参数(这里我们想传给线程处理函数什么参数,我就就可以填什么参数,但是要注意类型的转换
dwCreationFlags:线程的创建方式(立即创建或挂起等)
lpThreadID:这里我们只需要填上变量的地址,该函数会自动填入线程ID
返回值:如果创建成功,则返回线程句柄

  • 定义线程处理函数
    ThreadProc函数
    MSDN官方文档解释ThreadProc函数
    函数原型:
DWORD WINAPI ThreadProc(
	LPVOID lpParameter   //创建线程事,传递给线程处理函数的参数
);

三.线程实例(单线程,多线程)

单线程执行

我们来通过一段代码来看看单线程执行:

// 线程.cpp : 此文件包含 "main" 函数。程序执行将在此处开始并结束。
//

#include <iostream>
#include <windows.h>
using namespace std;
DWORD WINAPI TreadProc(LPVOID lpParameter);
int main()
{
    //创建线程
    char a[] = "1111111111111111111";
    int ID1 = 0;
    HANDLE hTread1 = CreateThread(NULL, 1, TreadProc, a, 0, (LPDWORD)ID1);
    return 0;
}
DWORD WINAPI TreadProc(LPVOID lpParameter) {
    while (1) {
        cout << lpParameter << endl;
        Sleep(1000);
    }
}

我们来执行这一段代码:
主线程退出
发现出现了错误,线程处理函数根本没有执行,或者说我们根本没有看到。
我们来分析一下问题所在:
我们不难发现,在创建了进程之后,主进程退出了,所以说我们创建的线程也退出了,我们可以使用一个阻塞函数getchar()来观察:

// 线程.cpp : 此文件包含 "main" 函数。程序执行将在此处开始并结束。
//

#include <iostream>
#include <windows.h>
using namespace std;
DWORD WINAPI TreadProc(LPVOID lpParameter);
int main()
{
    //创建线程
    char a[] = "Hello World";
    int ID1 = 0;
    HANDLE hTread1 = CreateThread(NULL, 1, TreadProc, a, 0, (LPDWORD)ID1);
    getchar();
    return 0;
}
DWORD WINAPI TreadProc(LPVOID lpParameter) {
    char* a = (char*)lpParameter;
    while (1) {
        cout << a << endl;
        Sleep(1000);
    }
}

这样我们就能看到单线程的执行了:
单线程执行

多线程执行

我们创建多个线程来看看多线程的执行:

// 线程.cpp : 此文件包含 "main" 函数。程序执行将在此处开始并结束。
//

#include <iostream>
#include <windows.h>
using namespace std;
DWORD WINAPI TreadProc(LPVOID lpParameter);
int main()
{
    //创建线程
    char a[] = "111111111111";
    int ID1 = 0;
    HANDLE hTread1 = CreateThread(NULL, 1, TreadProc, a, 0, (LPDWORD)ID1);
    char b[] = "222222222222";
    int ID2 = 0;
    HANDLE hTread2 = CreateThread(NULL, 1, TreadProc, b, 0, (LPDWORD)ID2);
    char c[] = "333333333333";
    int ID3 = 0;
    HANDLE hTread3 = CreateThread(NULL, 1, TreadProc, c, 0, (LPDWORD)ID3);
    getchar();
    return 0;
}
DWORD WINAPI TreadProc(LPVOID lpParameter) {
    char* a = (char*)lpParameter;
    while (1) {
        cout << a << endl;
        Sleep(1000);
    }
}

我们来看看执行效果:
多线程执行

四.挂起,销毁线程

  • 挂起线程
    我们在创建线程的时候,可以设置创建方式为CREATE_SUSPENDED也可以挂起线程
DWORD SuspendTread(
	HANDLE hThread    //线程句柄
);
  • 唤醒线程
DWORD ResumeThread(
	HANDLE hThread   //线程句柄
);
  • 结束指定线程
BOOL TerminateThread(
	HANDLE hThread,        //线程句柄
	DWORD dwExitCode       //退出代码
);
  • 结束函数所在线程
VOID ExitThread(
	DWORD dwExitCode       //退出代码
);

我们来写一个简单的双线程,一个处于挂起状态,一个处于执行状态,当跳过getchar() 函数后,交换两个线程的状态,以此来展示挂起线程和结束线程:

// 线程.cpp : 此文件包含 "main" 函数。程序执行将在此处开始并结束。
//

#include <iostream>
#include <windows.h>
using namespace std;
DWORD WINAPI TreadProc(LPVOID lpParameter);
int main()
{
    //创建线程
    char a[] = "111111111111";
    int ID1 = 0;
    HANDLE hThread1 = CreateThread(NULL, 1, TreadProc, a, 0, (LPDWORD)ID1);
    char b[] = "222222222222";
    int ID2 = 0;
    HANDLE hThread2 = CreateThread(NULL, 1, TreadProc, b,CREATE_SUSPENDED, (LPDWORD)ID2);
    getchar();
    SuspendThread(hThread1);
    ResumeThread(hThread2);
    getchar();
    return 0;
}
DWORD WINAPI TreadProc(LPVOID lpParameter) {
    char* a = (char*)lpParameter;
    while (1) {
        cout << a << endl;
        Sleep(1000);
    }
}

我们来看看执行效果:
挂起线程,唤醒线程

五.线程相关操作

  • 获取当前线程ID
    GetCurrentThreadID()函数
  • 获取当前线程句柄
    GetCurrentThread()函数
  • 等候单个句柄有信号
    这里介绍一下可等候句柄:只有当一个句柄有有信号和无信号两种状态时,才可以称为可等候信号
    例:线程句柄:当线程执行时,为无信号状态,当线程结束时,为有信号状态
VOID WaitForSingleObject(
	HANDLE handle,             //句柄BUFF地址(对象的句柄)
	DWORD dwMilliseconds       //最长等候时间,当设置为INIFINITE时,永无超时
);

MSDN官方文档解释WaitForSingleObject函数
当无信号时,该函数为阻塞函数。

  • 同时等候多个句柄信号
    MSDN官方文档解释WaitForMultipleObject函数
WaitForMultipleObjects(
	DWORD nCount,               //句柄数量
	CONST HANDLE *lpHandles,    //句柄数组地址
	BOOL bWaitAll,              //等候方式
	DWORD dwMilliseconds        //最大等候时间(如果设定为INFINITE,则永无超时
);
 bWaitAll--等候方式:
 TRUE:所有句柄有信号才结束等候
 FALSE:所有句柄中只要有一个有信号,就结束等候

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.coloradmin.cn/o/515106.html

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

ElasticSeach 集成 springboot

声明是ElasticSearch? ElasticSearch是一个基于Lucene的搜索服务器。它提供了一个分布式多用户能力的全文搜索引擎&#xff0c; 基于RESTful web接口。Elasticsearch是用Java开发的&#xff0c;并作为Apache许可条款下的开放源码发布&#xff0c;是 当前流行的企业级搜索引擎…

大数据 | (五)通过Sqoop实现从MySQL导入数据到HDFS

知识目录 一、前言二、导入前的准备2.1 Hadoop集群搭建2.2 Hadoop启停脚本 三、docker安装MySQL四、安装Sqoop4.1 Sqoop准备4.2 Sqoop连接Mysql数据测试 五、导入MySQL数据到hdfs5.1 准备MySQL数据5.2 导入数据 六、Sqoop现状七、结语 一、前言 各位CSDN的朋友们大家好&#x…

5.11组会衍生总结:train/eval/BN、CNN与特征图、极大似然与EM、方差n与n-1(有偏估计/无偏估计)

目录组会问题:1.关于模型的train/eval与batchnorm1-1.理论1-2.实际运用(包含loss反向传播)2.CNN详解,特征图是什么CNN处理过程特征图(也叫通道)(num_features)总结(包含CNN图片的规律分析):3.极大似然估计与EM最大期望4.方差的n与n-1(有偏估计与无偏估计) 组会问题:…

企业文化和品牌文化是两回事

商业通常谈两类文化&#xff1a;企业文化&#xff0c;品牌文化 1&#xff09;组织内部的文化 2&#xff09;品牌以产品为依托&#xff0c;给消费群体营造的文化 “积极稳定”的文化氛围打造是个慢活 企业文化&#xff0c;既要挂在墙上&#xff0c;又要挂在嘴上&#xff0c;最终…

二叉查找树和平衡二叉树

二叉查找树 下面是一张数据库的表,有两列,分别是 Col1 和 Col2 我们来查找一下col289的这行数据,SQL语句如下: select * from a where col2 87没有用索引时执行上面的查询 , 数据从磁盘一条一条拿来对比最终找到结果&#xff0c;如果数据表很大,数据又在表尾的话,需要花费非…

Open3D点云数据处理(一):VSCode配置python,并安装open3d教程

文章目录 1 python下载与安装1.1 python下载1.2 python安装1.3 验证python是否安装成功 2 VSCode下载与安装2.1 下载2.2 安装2.3 安装汉化插件2.4 vscode安装python扩展2.5 编写一个简单的python程序并运行2.6 在外部终端中打印运行结果2.7 测试代码&#xff1a;使用python画一…

常用的网页设计工具,有哪些比较推荐

网页设计并不容易&#xff0c;易于使用的网页设计工具更难找到。随着网络的快速发展&#xff0c;网站迅速崛起&#xff0c;网页设计也很流行。本文收集了7款易于使用的网页设计工具&#xff0c;每一种近年来都受到网页设计师的广泛欢迎&#xff0c;以确保实用和易于使用。我希望…

背包九讲(dp问题详解)

一、01背包问题 首先了解一下题目&#xff1a; 有 N 件物品和一个容量是 V 的背包。每件物品只能使用一次。 第 i 件物品的体积是 vi&#xff0c;价值是 wi。 求解将哪些物品装入背包&#xff0c;可使这些物品的总体积不超过背包容量&#xff0c;且总价值最大。 输出最大价值。…

Java中的抽象类介绍

Java中的抽象类介绍 抽象类可以包含普通类的成员&#xff0c;它可以包含普通的属性、方法和内部类等成员。这些成员既可以被抽象类的子类继承和使用&#xff0c;也可以被抽象类自身使用。抽象类中的非抽象方法必须要有具体实现&#xff0c;否则无法通过编译。抽象类中也可以拥…

Leetcode2379. 得到 K 个黑块的最少涂色次数

Every day a Leetcode 题目来源&#xff1a;2379. 得到 K 个黑块的最少涂色次数 解法1&#xff1a;滑动窗口 首先题目给出一个下标从 0 开始长度为 n 的字符串 blocks&#xff0c;其中 blocks[i] 是 ‘W’ 或者 ‘B’ &#xff0c;分别表示白色块要么是黑色块。 现在我们可…

抗体偶联药物都有哪些?(详细名单)

抗体偶联药物ADC简介 抗体-药物偶联物或ADC是一类生物制药药物&#xff0c;设计用于治疗癌症的靶向疗法。与化学疗法不同&#xff0c;ADC 旨在靶向并杀死肿瘤细胞&#xff0c;同时保留健康细胞。截至 2023 年5月&#xff0c;约有 433 家制药公司正在开发 ADC。 ADC 是由与具有…

SPI基础

SPI硬件接口 SPI协议使用3条总线以及片选线。3条总线分别是SCK、MOSI、MISO&#xff0c;片选线为SS(NSS、CS) SPI基础属性 主从模式&#xff1a;主机模式&#xff0c;从机模式 通讯频率&#xff1a;不定&#xff0c;根据设备速率确定 数据位数&#xff1a;4位、7位&#xff…

基于狮群算法优化的核极限学习机(KELM)分类算法-附代码

基于狮群算法优化的核极限学习机(KELM)分类算法 文章目录 基于狮群算法优化的核极限学习机(KELM)分类算法1.KELM理论基础2.分类问题3.基于狮群算法优化的KELM4.测试结果5.Matlab代码 摘要&#xff1a;本文利用狮群算法对核极限学习机(KELM)进行优化&#xff0c;并用于分类 1.KE…

【华为机试】——每日刷题经验分享

【华为机试】——每日刷题经验分享&#x1f60e; 前言&#x1f64c;题目&#xff1a;HJ9 提取不重复的整数 总结撒花&#x1f49e; &#x1f60e;博客昵称&#xff1a;博客小梦 &#x1f60a;最喜欢的座右铭&#xff1a;全神贯注的上吧&#xff01;&#xff01;&#xff01; &a…

被ChatGPT“抢饭碗”的人

ChatGPT问世至今&#xff0c;互联上讨论声绵延不绝。有人说&#xff0c;AI会替代基础性工作。 一名金融从业者对《橡果商业评论》表示&#xff0c;这实际上是对人类经验的替代&#xff0c;那些引以为傲的经验&#xff0c;来源于对历史的总结&#xff0c;AI出现后&#xff0c;“…

【C++STL】红黑树(更新中)

前言 上篇博客学习了平衡二叉搜索树(AVLTree)&#xff0c;了解到AVL树的性质&#xff0c;二叉搜索树因为其独特的结构&#xff0c;查找、插入和删除在平均和最坏情况下都是O(logn)。AVL树的效率就是高在这个地方。 但是在AVL树中插入或者删除结点&#xff0c;使得高度差的绝对…

【GO 编程语言】数组

数组 文章目录 数组一、数组是什么二、初始化数组三、数组的遍历四、数组类型五、数组排序 一、数组是什么 Go 语言提供了数组类型的 数据结构。 数组是具有相同唯一类型的一组已编号且长度固定的数据项序列,这种类型可以是任意的原始类型。例如整型、字符串或者自定义类型。 …

HTTP第八讲——请求方法

目前 HTTP/1.1 规定了八种方法&#xff0c;单词都必须是大写的形式 GET&#xff1a;获取资源&#xff0c;可以理解为读取或者下载数据&#xff1b;HEAD&#xff1a;获取资源的元信息&#xff1b;POST&#xff1a;向资源提交数据&#xff0c;相当于写入或上传数据&#xff1b;P…

echarts 如何保存为图片时,如何同时保存滚动条隐藏的数据

echarts 如何保存为图片&#xff0c;如何保存滚动条隐藏的数据 效果展示上代码内容讲解面临的问题解决思路&#xff08;当前代码的思路&#xff09; 效果展示 这是直接将保存的图片显示在网页下方了 上代码 <!DOCTYPE html> <html><head><meta charse…

【2023/05/11】Edsger Dijkstra

Hello&#xff01;大家好&#xff0c;我是霜淮子&#xff0c;2023倒计时第6天。 Share Sorrow is hushed into peace in my heart like the evening among thr silent trees. 译文&#xff1a; 忧思在我的心里平静下去&#xff0c;正如暮色降临在寂静的山林中。 Some unsee…