【Windows线程开发】线程基础

这篇具有很好参考价值的文章主要介绍了【Windows线程开发】线程基础。希望对大家有所帮助。如果存在错误或未考虑完全的地方,请大家不吝赐教,您也可以点击"举报违法"按钮提交疑问。

本篇文章来带领大家了解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);
    }
}

我们来执行这一段代码:
【Windows线程开发】线程基础
发现出现了错误,线程处理函数根本没有执行,或者说我们根本没有看到。
我们来分析一下问题所在:
我们不难发现,在创建了进程之后,主进程退出了,所以说我们创建的线程也退出了,我们可以使用一个阻塞函数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);
    }
}

这样我们就能看到单线程的执行了:
【Windows线程开发】线程基础

多线程执行

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

// 线程.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);
    }
}

我们来看看执行效果:
【Windows线程开发】线程基础

四.挂起,销毁线程

  • 挂起线程
    我们在创建线程的时候,可以设置创建方式为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);
    }
}

我们来看看执行效果:
【Windows线程开发】线程基础

五.线程相关操作

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

MSDN官方文档解释WaitForSingleObject函数
当无信号时,该函数为阻塞函数。文章来源地址https://www.toymoban.com/news/detail-440692.html

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

到了这里,关于【Windows线程开发】线程基础的文章就介绍完了。如果您还想了解更多内容,请在右上角搜索TOY模板网以前的文章或继续浏览下面的相关文章,希望大家以后多多支持TOY模板网!

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处: 如若内容造成侵权/违法违规/事实不符,请点击违法举报进行投诉反馈,一经查实,立即删除!

领支付宝红包 赞助服务器费用

相关文章

  • 【postgresql 基础入门】pgAdmin4 最新图形化客户端的安装使用,值得推荐的数据库开发工具,还有大家喜欢的暗黑主题

    ​ 专栏内容 : postgresql内核源码分析 手写数据库toadb 并发编程 ​ 开源贡献 : toadb开源库 个人主页 :我的主页 管理社区 :开源数据库 座右铭:天行健,君子以自强不息;地势坤,君子以厚德载物. 入门准备 postgrersql基础架构 快速使用 初始化集群 数据库服务管理 psql客户

    2024年02月07日
    浏览(43)
  • 【多线程】定时器,详解定时器原理,让大家更深刻的理解多线程

    前言: 大家好,我是 良辰丫 ,今天我们一起了解一下定时器,通过定时器来熟悉一下线程安全等相关知识点.💞💞💞 🧑个人主页:良辰针不戳 📖所属专栏:javaEE初阶 🍎励志语句:生活也许会让我们遍体鳞伤,但最终这些伤口会成为我们一辈子的财富。 💦期待大家三连,关注

    2024年02月01日
    浏览(60)
  • 大家都说Java有三种创建线程的方式!并发编程中的惊天骗局!

    在Java中,创建线程是一项非常重要的任务。线程是一种轻量级的子进程,可以并行执行,使得程序的执行效率得到提高。Java提供了多种方式来创建线程,但许多人都认为Java有三种创建线程的方式,它们分别是 继承Thread类、实现Runnable接口和使用线程池。 但是,你们知道吗?

    2024年02月08日
    浏览(71)
  • 【Android开发基础】多线程-Handle消息机制

    描述:一个静态的UI界面,如果需要同时加载多个资源,并能在视觉上得到动态反馈,就需要用到Handle消息机制。 知识点:Handle消息机制 难度:初级 理解 1、为什么要用Handle 根据官方规则:子线程不允许操作主线程中的组件 2、原理图 3、关键对象 (1)Message(消息类)   

    2024年02月09日
    浏览(39)
  • Windows安装pip和pygame详细教程(附国内下载地址),java多线程基础面试题

    3、安装pip 解压pip压缩包 进入解压后的目录 安装pip 加入系统变量 D:Pythondir 驱动器 D 中的卷是 Data 卷的序列号是 4662-8122 D:Python 的目录 … 2018/08/10 12:01 1,197,370 pip-9.0.1.tar.gz … D:Pythontar xf pip-9.0.1.tar.gz D:Pythoncd pip-9.0.1 D:Pythonpip-9.0.1dir … 2016/11/07 02:49 2,934 setup.py … D:Pythonp

    2024年04月24日
    浏览(39)
  • 云原生是什么?和Docker、K8s是什么关系?又带来了何种影响?希望这篇文章给自己及大家解点疑惑

    现在容器化和云原生十分火爆,但如果要理解为什么这个技术在近几年突然爆火,身为传统的Springboot和Springcloud体系开发者都有很多困惑,怎么就突然这么火爆了呢?诸如我就产生了以下问题: 传统的springboot或springcloud体系和云原生对比起来有何差别? 传统的spring体系和云

    2024年02月07日
    浏览(41)
  • 前端新手Vue3+Vite+Ts+Pinia+Sass项目指北系列文章 —— 第十一章 基础界面开发 (组件封装和使用)

    Vue 是前端开发中非常常见的一种框架,它的易用性和灵活性使得它成为了很多开发者的首选。而在 Vue2 版本中,组件的开发也变得非常简单,但随着 Vue3 版本的发布,组件开发有了更多的特性和优化,为我们的业务开发带来了更多便利。本文将介绍如何使用 Vue3 开发业务组件

    2024年02月19日
    浏览(89)
  • 初入公司用不好git ?-- 本篇针对GitLab

    本篇并不涉及git的所有知识,内容包括工作中每天用到的以及需要知道的 一、从远程仓库拉取指定分支到本地仓库,并创建个人分支  二、(补充)基于以上补充几点基础知识点以便你更好理解并实践 1. 主分支:通常是master分支 2. 开发分支:基于主分支派生,你通常在这个

    2024年02月05日
    浏览(48)
  • 无人机竞赛视觉算法开发流程开源计划(询问大家意见)

    本科中参加过一系列的无人机机器人竞赛,像电赛、工训赛、机器人大赛这些,有一些比较常用的方案打算开源一下。现在读研了,也算是对本科的一个总结,但是还是想看看大家意见,大家有什么需求可以在评论区说,我会多参考,这周我会先整理,下周会放出博客与git

    2024年02月20日
    浏览(38)
  • 带领你打开C++神秘之门--入门篇

      c++ 我来了,恭喜牛牛解锁新世界.开启c++的学习之旅. 🎈个人主页:🎈 :✨✨✨初阶牛✨✨✨ 🐻推荐专栏: 🍔🍟🌯C语言进阶 🔑个人信条: 🌵知行合一 🍉本篇简介::讲解C++入门知识,命名空间的引入,C++中的输入输出,缺省参数的出现,函数重载等. 金句分享: ✨花会沿路盛开

    2024年02月08日
    浏览(41)

觉得文章有用就打赏一下文章作者

支付宝扫一扫打赏

博客赞助

微信扫一扫打赏

请作者喝杯咖啡吧~博客赞助

支付宝扫一扫领取红包,优惠每天领

二维码1

领取红包

二维码2

领红包