首页 > 编程知识 正文

c语言有多线程,C语言实现多线程

时间:2023-05-03 18:49:12 阅读:243791 作者:1140

序言

实验室项目采用多线程实现,然而暂时只涉及到几个基本的线程操作函数,线程和进程的区别、线程的同步和异步机制以及线程通信等暂时都没有涉及,打算在这里做些总结,以备后用。
1. 进程

是一种抽象的概念,从来没有统一的标准定义:

        1    进程是一个具有一定独立功能的程序关于某个数据集合的一次运行活动;
        2    进程是一个独立的可调度的活动;
        3    进程是可以并行执行的计算单位;
        4    进程是一个抽象实体,当它执行某个任务时,要分配和释放各种资源;

通常定义:进程是一个具有一定独立功能的程序在一个数据集上的一次动态执行的过程,是操作系统进行资源分配和调度的一个独立单位,是应用程序运行的载体。

    进程和程序是有本质区别的:
        程序是静态的。它是一些保存在磁盘上的指令的有序集合,没有任何执行的概念;
        进程是一个动态的概念。它是程序执行的过程,包括了动态创建、调度和消亡的整个过程;是程序执行和资源管理的最小单位。

讲的简单点,以下代码执行时

int main()
{
    printf(”pid is %d/n”,getpid() );
    return 0;
}

进入main函数,这就是一个进程,进程pid会打印出来,然后运行到return,该函数就退出,由于该函数是该进程的唯一的一次执行,所以return后,该进程也会退出。

    进程的组成:程序 + 数据集合 + PCB…
        程序用于描述进程要完成的功能,是控制进程执行的指令集;
        数据集合是程序在执行时所需要的数据和工作区;
        程序控制块(Program Control Block,简称PCB),包含进程的描述信息和控制信息,是进程存在的唯一标志。

    Linux下进程的分类:交互进程 + 批处理进程 + 守护进程
        交互进程:由shell启动的进程。交互进程既可以在前台运行,也可以在后台运行。
        批处理进程:这种进程和终端没有联系,它是被提交到一个队列中的进程序列。
        守护进程:又称监控进程,也就是通常说的Daemon进程,是Linux中的后台服务进程。它是一个生存期较长的进程,通常独立于控制终端并且周期性地执行某种任务或等待处理某些发生的事件。守护进程常常在系统引导装入时启动,在系统关闭时终止。

    进程的基本特征:动态性 + 并发性 + 独立性 + 结构性
        动态性:进程是程序的一次执行过程,是临时的,有生命期的,是动态产生,动态消亡的;
        并发性:任何进程都可以同其他进程一起并发执行;
        独立性:进程是系统进行资源分配和调度的一个独立单位;
        结构性:进程由程序、数据和进程控制块三部分组成。

    进程的状态:创建 + 就绪 + 运行 + 阻塞(等待) + 退出
        创建:进程正在创建,还不能运行。操作系统在创建进程时要进行的工作包括分配和建立进程控制块表项、建立资源表格并分配资源、加载程序并建立地址空间;
        就绪:进程已经具备执行的一切条件,正在等待分配CPU的处理时间片,或者时间片已用完,此线程被强制暂停,等待下一个属于它的时间片到来;
        运行:此进程正在执行,正在占用CPU时间片;
        阻塞:也叫等待状态,等待某一事件(如IO或另一个线程)执行完;等待态又分为可中断等待和不可中断等待两种。可中断的等待进程可被信号中断,而不可中断的等待进程不能被信号中断;
        退出:进程已结束,所以也称结束状态,释放操作系统分配的资源。

    进程在运行过程有三种状态:就绪、运行、阻塞,创建和退出状态描述的是进程的创建过程和退出过程。


2. 线程

早期的操作系统中并没有线程的概念,进程是能拥有资源和独立运行的最小单位,也是程序执行的最小单位。任务调度采用的是时间片轮转的抢占式调度方式,而进程是任务调度的最小单位,每个进程有各自独立的一块内存,使得各个进程之间内存地址相互隔离。
后来,随着计算机的发展,对CPU的要求越来越高,进程之间的切换开销较大,已经无法满足越来越复杂的程序的要求了。于是就发明了线程,线程是程序执行中一个单一的顺序控制流程,是程序执行流的最小单元,是处理器调度和分派的基本单位。

定义:通常线程指的是共享相同地址空间的多个任务
通俗:带有时间片的函数

    线程共享的资源
        可执行指令(代码)
        静态数据
        进程中打开的文件描述符
        当前的工作目录
        用户ID
        用户组ID

    线程独占的资源
        线程ID
        PC(程序计数器)和相关寄存器
        堆栈
        错误号(errno)
        优先级
        执行状态和属性

    线程与进程关系示意图
        共享进程的地址空间,全局变量(数据和堆)。各个线程共享进程的堆区,而线程各自维持自己的栈
        在操作共享区域的时候才有可能出现同步需要,操作栈不需要同步
        一个程序至少有一个进程,一个进程至少有一个线程

    线程的堆栈问题
        线程就是个无产阶级,但无产阶级干活,总得有自己的劳动工具吧,这个劳动工具就是栈,线程有自己的栈,这个栈仍然是使用进程的地址空间,只是这块空间被线程标记为了栈。
        每个线程都会有自己名义上私有的堆栈,之所以说是名义上的,是由于这些线程属于同一个进程,其他线程只要获取了你的私有堆栈上某些数据的指针,其他线程便可以自由访问你名义上的私有空间的数据变量,不过必须做好同步保护。而且一般不这样做,因为容易造成堆内内存管理混乱破坏线程栈空间等问题。

    线程与进程的区别
        地址空间:进程有自己独立的地址空间,线程共享进程的地址空间
        资源拥有:进程是资源分配和拥有的单位,同一个进程内的线程共享进程的资源
        并发性:均可并发执行。但线程的划分尺度小于进程,使得多线程程序的并发性高
        独立性:进程之间相互独立,线程不能独立执行。每个独立的线程有一个程序运行的入口、顺序执行序列和程序的出口,必须依存在应用程序中,由应用程序提供多个线程执行控制。
        调度和切换:线程是程序执行的最小单位,而进程是操作系统分配资源的最小单位。线程上下文切换比进程上下文切换要快得多

    线程和进程的优势比较

    线程的状态:创建 + 就绪 + 运行 + 阻塞(等待) + 退出
        创建:一个新的线程被创建,等待该线程被调用执行;
        就绪:时间片已用完,此线程被强制暂停,等待下一个属于他的时间片到来;
        运行:此线程正在执行,正在占用CPU时间片;
        阻塞:也叫等待状态,等待某一事件(如IO或另一个线程)执行完;
        退出:一个线程完成任务或者其他终止条件发生,该线程终止进入退出状态,退出状态释放该线程所分配的资源。

    早期的进程相当于现在的只有单个线程的进程,现在的多线程也有五种状态,现在的多线程的生命周期与早期进程的生命周期类似。

    虚拟地址、逻辑地址、线性地址、物理地址的区别?
    解析: 分段机制把一个逻辑地址转换为线性地址;接着,分页机制把一个线性地址转换为物理地址。
    逻辑地址(启动分段) -> 线性地址(启动分页) -> 物理地址
        虚拟地址:虚拟内存映射出来的地址
        逻辑地址:程序的段加偏移量形成的,C/C++程序中取地址求出来的地址就是逻辑地址
        线性地址:是逻辑地址到物理地址的中间层,只有启动分页机制的时候才有线性地址,如果没有分页机制,那么线性地址就是物理地址
        物理地址:是内存中实实在在存在的硬件地址

补充: Linux进程间通信 - IPC
由于多进程要并发协调工作,进程间的同步,通信是在所难免的。
linux下进程间通信的几种主要手段简介:

(1) 管道(Pipe)及有名管道(named pipe):管道可用于具有亲缘关系进程间的通信,有名管道克服了管道没有名字的限制,因此,除具有管道所具有的功能外,它还允许无亲缘关系进程间的通信
(2) 信号(Signal):信号是比较复杂的通信方式,用于通知接受进程有某种事件发生,除了用于进程间通信外,进程还可以发送信号给进程本身
(3) 报文(Message)队列(消息队列):消息队列是消息的链接表,包括Posix消息队列system V消息队列。有足够权限的进程可以向队列中添加消息,被赋予读权限的进程则可以读走队列中的消息。消息队列克服了信号承载信息量少,管道只能承载无格式字节流以及缓冲区大小受限等缺点。
(4) 共享内存:使得多个进程可以访问同一块内存空间,是最快的可用IPC形式。是针对其他通信机制运行效率较低而设计的。往往与其它通信机制,如信号量结合使用,来达到进程间的同步及互斥。
(5) 信号量(semaphore):主要作为进程间以及同一进程不同线程之间的同步手段。
(6) 套接口(Socket):更为一般的进程间通信机制,可用于不同机器之间的进程间通信。起初是由Unix系统的BSD分支开发出来的,但现在一般可以移植到其它类Unix系统上:Linux和System V的变种都支持套接字。

多线程间要通信,应该怎么做?多数的多线程都是在同一个进程下的,它们共享该进程的全局变量,我们可以通过全局变量来实现线程间通信。如果是不同的进程下的2个线程间通信,直接参考进程间通信。

 

Acknowledgements:
http://blog.csdn.net/rl529014/article/details/51280018
http://blog.csdn.net/hairetz/article/details/4281931/
http://blog.csdn.net/daoshuti/article/details/55045567
http://blog.csdn.net/luoweifu/article/details/46595285
http://www.cnblogs.com/LUO77/p/5771237.html

2017.04.10
本人博客会根据个人知识升级情况进行补充修改
---------------------
作者:shuaixio
来源:CSDN
原文:https://blog.csdn.net/baidu_35692628/article/details/69218808
版权声明:本文为博主原创文章,转载请附上博文链接!

版权声明:该文观点仅代表作者本人。处理文章:请发送邮件至 三1五14八八95#扣扣.com 举报,一经查实,本站将立刻删除。