以线程为单位看程序,流程梳理 windows操作系统

线程

线程是什么,为什么要用线程

        编程是为了实现相应功能的,所以线程可以先从功能去认识。

        函数和线程都可运行自身内部函数和代码,线程的不同是——函数运行等待时,不能进行另一个相同函数的操作,因为函数只有一个;线程运行时,若等待,仍可进行其他线程的使用。

        所以我们可以把线程看成,自动创建不同函数,运行同一内容,这样就不会阻碍其他线程进行,从而做到能够并行或并发。

线程的两种运行方式:并行、并发

        并行,多个线程同时开始;

        并发,多个线程同时开始,轮换进行。

注1:不一定同时结束。

注2:单核CPU不能并行,只是轮换的非常快。

线程的状态

        就绪 阻塞 运行(新生 死亡)

        状态转化

        就绪->运行:获得CPU时间片(该进程在CPU上运行时间)

        (反)运行->就绪:失去时间片/中断切换/轮换进程/重新开始

        运行->阻塞:等待IO操作,主动写出相应操作,使其转化。(操作和命令的输入输出,包括运行某一功能)

        (反)阻塞->运行:阻塞条件解除,就绪,运行。

        阻塞->就绪:IO操作发生

        (反)就绪->阻塞:由于某些原因,线程暂不可运行,处于阻塞状态。

        总:当线程需要分配到CPU和进行IO操作都会阻塞

线程的地位

        线程是操作系统能运算的最小单位,是进程中的一个执行单元,是分配CPU的基本单位。

线程的组成部分

        线程栈:

        其中储存了线程所需的局部临时资源。

        内核对象:

        操作系统可以通过内核对象管理和调度线程核心数据结构,包含有关于此的信息。其中有一个计数器,初始值为1,有进程访问时+1,线程退出时-1,关闭句柄时-1,为0时自动销毁。

如何结束线程

  1. 自然返回
  2. ExitThread() 结束调用者
  3. TerminateThread() 强制杀死线程

进程

线程构成了进程

        多个线程的操作的集合就是进程。

        一个进程至少有一个线程,被叫做主线程,主线程是我们写的程序的入口点,写入主要逻辑。

进程的地位

        进程是一个操作系统的执行实体,是CPU调度和执行的基本单位,是分配资源的一个独立的集合(这里倾向于操作系统角度,线程是CPU角度)

进程对线程的作用

        如果线程看为一个函数,进程可以看成一个简化的项目,这个项目是一体的,把线程组合到了一起。

        这样可以减少零散的轮换,提升处理效率;共享地址空间和全局变量;访问共享空间。来减少代码定义的复杂度。

进程储存在哪里

        进程分32位系统和64位系统两种。

        32位系统进程,加载4GB虚拟地址空间,虚拟地址空间是逻辑上的,只有某部分需要时才会映射到物理内存上,本身并非直接占用。

        而这个空间的分布为三种模式

        空指针模式,

        用户模式,

        内核模式。

 

        而64位的进程虚拟空间是8TB

        而处于空间大小和系统优化原因,64位的已不要求重点记忆空间内容。

        关于线程和进程的具体调用约定推荐去官网查询。

程序

程序是什么

        程序是一种静态的指令,程序内写了包含进程的一系列代码,进程是一种运行状态。

程序的编译过程

预处理

  1. 解析#ifdef/ifndef
  2. 解析#include,将文件加载到当前位置
  3. 解析#define,简单文本替换,无安全校验检查
  4. 删除所有注释
  5. 对文件进行序号标识
  6. #pragma pack(n)(结构体或联合体的成员以n字节对齐)

编译

        1.词法分析(将源代码分割成词法单元,如关键字,标识符,运算符)

        2.语法分析(将词法单元转化为抽象语法树,是语法元素的关系的连接结构)

        3.语义分析(检查语义正确性,如类型、作用域)

        4.中间代码生成(与编译器有关,可能进行)(更换更适合优化的代码来过度)

        5.优化(尝试更改程序性能、可读性或其他特定属性,可发生在多个层次,如中间代码优化、指令级优化)

        6.得到汇编语言代码(文本文件方式储存)

汇编

        代码生成(二进制)(以汇编语言代码生成目标代码(Obj.文件))(若为单模块程序,可直接从源代码转化为机械码,不需要汇编和链接)

链接

        连接器进行代码组织和链接(多模块程序的目标代码结合)

线程同步

        上面已经从线程的角度介绍了程序,接下来介绍重要的用法。

        线程同步可以形容操作,也可以形容状态。

        操作:通过使用锁、信号量、条件变量等机制,使线程处于阻塞状态,让线程相互协作,互不干扰。

        而这种协作状态也可以叫线程同步。

实现线程同步的方法

        为了实现线程同步,线程要保持某一操作,在某一时间只属于自己,来实现相关运算,否则用被其他线程篡改,很容易出现错误。

        而这个某时只能运行一个的操作就是原子访问状态。

原子访问

        古希腊有一个原子不可分的假说,大概就是这个名字的由来。

        原子访问的变量在同一时间,只能由一个线程运行,其他线程运行到这里会发生阻塞,直至唯一的原子变量运行后,再进行对其的原子访问。

        C++中参考 std::atomic<int/long/char>  VariableName(n),

        其中分别为类型、变量名、赋值数。也可以去除括号,用等于号赋值。

关键段

        显然,只使一个变量有原子性是不够的。

        我们需要创建一个区域,让这个区域的代码有原子性,这样的代码段,才能更有效的实现线程同步,我们把这段代码叫做关键段(也叫临界区)。

如何实现关键段
关键段函数

        有一个<windows.h>的关键段函数,可以实现该操作,

        CRITICAL_SECTION  A(变量名);   (用来定义变量)

        InitializeCriticalSection(&A);         (初始化)

        EnterCriticalSection(&A);             进入关键段

        LeaveCriticalSection(&A);             退出关键段

        Delete CriticalSection(&A);            释放

注:此函数不能跨进程。

同步原语

        同步原语分为几种,他们都能实现有关关键段的功能。(它们也可以分在内核对象类,但内核对象侧重于,内核对它们的调度和支持)

        头文件依旧<windows.h>

条件变量

        CONDITION_VARIABLE condVar(条件变量对象)

        (VOID)  InitializeConditionVariable(&condVar)(初始化)

        (BOOL)  SleepConditionVariableCS(&condVar,&cs,INFINITE) (关键段内等待条件变量,使线程处于休眠状态) (cs为定义的关键段)

        (VOID)   WakeConditionVariable(&condVar) (唤醒某正在等待的线程)

        (VOID)   WakeAllConditionVariable(&condVar)  (唤醒所有在条件变量上的线程)

互斥量

        HANDLE hMutex ;  //定义

        hMutex = CreateMutex(NULL,FALSE,NULL);   //创建

        (参数为安全属性、初始状态、名称)

        (初始状态代表是否锁定第一个创建它的线程)

        WaitForSingleObject(hMutex ,INFINITE);   //用来等信号,锁被占用时无信号。下面两个同步原语一致

        (第二个参数为等待时间,单位毫秒,这里是一直等)

        (返回值有WAIT_OBJECT_0成功;WAIT_ABANDONED有互斥量线程终止且未释放;WAIT_TIMEOUT超时;WAIT_FALED失败)

        ReleaseMutex(hMutex );  //使状态为0,解锁;

事件

        HANDLE hEvent;

        CreateEvent(hEvent);

        Waitforsingleobject(hEvent);

        SetEvent(hEvent);

        ResetEvent(hEvent);

        (事件不同之处在于,它可以定义是否有信号)

信号量

        CreateSemaphore(

        LPSECURITY_ATTRIBUTES lpSemaphoreAttributes,  //通常使用NULL,为不可被继承

          LONG                  lInitialCount,  //最初可以访问共享资源的线程数量

          LONG                  lMaximumCount,  //上面信号量的最大计数值

          LPCSTR                lpName  //信号量名称,可NULL

        Waitforsingleobject();  (每使用时,上文信号量值减1)

        BOOL ReleaseSemaphore(

        HANDLE hSemaphore,  //创建的HANDLE类型返回值

          LONG   lReleaseCount,  //释放信号量数(解锁数)

          LPLONG lpPreviousCount  //接收之前信号量的计数值,可NULL不使用

);

关键段与内核对象的区别

        由于同步原语的内核性(被叫做内核对象),它们与关键段(指函数)有一定区别。

        1.作用范围

        关键段:用户模式;

        内核对象:内核模式。

        2.灵活性、安全性:

        内核对象更好(因为阻塞事件、强制杀死时自动释放)

        3.效率:

        关键段更好(无需切换状态--内核与用户)

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值