zoukankan      html  css  js  c++  java
  • 进程 & 线程相关知识

    不管Java,C++都有进程、线程相关的内容。在这里统一整理吧。

    Python的线程,其实是伪线程,不能真正的并发。下面也有讲。

    线程自己基本上不拥有系统资源,只拥有一点在运行中必不可少的资源(如程序计数器,一组寄存器)。

    多个线程共享内存。

    参考了这篇文章:http://www.cnblogs.com/qiaoconglovelife/p/5319779.html

    • 进程与PCB

        进程:进程是程序的一次执行过程,是系统进行资源分配和调度的一个独立单位。

        进程实体(进程映像):由程序段、相关数据段和PCB三部分构成。进程是动态的,进程实体是静态的。

        PCB(进程控制块):系统利用PCB来描述进程的基本情况和运行状态,进而控制和管理进程;所谓创建进程,实际上是创建进程映像中的PCB;PCB是进程存在的唯一标志。

    进程有5种状态,其中前3种是基本状态:

    运行态、就绪态、阻塞态(等待态)。另两种是新建态终止态。

    • 进程的创建过程

        (1)分配ID与PCB:为新进程分配一个唯一的进程标识号,并申请一个空白的PCB(PCB是有限的)。若PCB申请失败则创建失败。

        (2)分配资源:为新进程的程序数据、以及用户栈分配必要的内存空间(在PCB 中体现)。注意:这里如果资源不足(比如内存空间),并不是创建失败,而是处于阻塞态。

        (3)初始化PCB:主要初始化(1)标志信息(2)处理机状态信息(3)处理机控制信息,以及(4)设置进程的优先级等。

        (4)调度:如果进程就绪队列能够接纳新进程,就将新进程插入到就绪队列,等待被调度运行。

        注意,进程的创建是一个原子操作,执行期间不允许中断,它是一个不可分割的基本单位。

    • 进程的终止

        引起进程终止的事件主要有:

        (1)正常结束

        (2)异常结束:如存储区越界、非法指令、I/O故障等

        (3)外界干预:如操作员或操作系统干预、父进程请求、父进程终止。

        操作系统终止进程的过程如下:

        (1)根据被终止进程的ID,检索PCB,从中读出该进程的状态

        (2)若被终止进程处于执行状态,立即终止该进程的执行,将处理机资源分配给其他进程

        (3)若该进程还有子进程,则应将其所有的子进程终止

        (4)将该进程所拥有的资源,或归还给其父进程或归还给操作系统

        (5)将该PCB从所在队列(链表)中删除。

    • 进程之间的切换

        (1)保存处理机上下文,包括程序计数器和其他寄存器

        (2)更新PCB信息

        (3)把进程的PCB移入相应的队列,如就绪、在某事件阻塞等队列。

        (4)选择另一个进程执行,并更新其PCB。

        (5)更新内存管理的数据结构。

        (6)恢复处理机上下文。

        注意:“调度”和“切换”的区别:调度是指决定资源分配给哪个进程的行为,是一种决策行为;切换是指实际分配的行为,是执行行为。一般来说,等有资源的调度,再有进程的切换。

    • 线程

        线程是轻量化的进程,是程序执行流的最小单位;由线程ID程序计数器寄存器集合堆栈组成;线程自己不拥有系统资源,只拥有一点在运行中必不可少的资源,但它可与同属一个进程的其他线程共享进程所拥有的全部资源。

    • 进程与线程区别

        (1)一个程序至少有一个进程,一个进程至少有一个线程。线程(Thread)是进程的一个实体,是CPU调度和分派的基本单位;

        (2)进程拥有独立的内存单元,而多个线程共享内存。从而线程效率更高;

        (3)进程有独立的地址空间,一个进程崩溃后,在保护模式下不会对其它进程产生影响,而线程没有单独的地址空间,一个线程死掉就等于整个进程死掉,所以多进程的程序要比多线程的程序健壮;

        (4)进程切换时,耗费资源较大,效率要差一些;

        (5)进程是系统资源分配的基本单位,线程是调度的基本单位。

    线程独有的内容:线程上下文,包括线程ID栈指针PC(程序计数器)、通用目的寄存器、条件码。

    线程共享的内容:文件描述符和整个用户虚拟地址空间,包括只读文本(代码)、静态变量、堆、所有的共享库代码和数据区域组成。

    • 相比进程,线程有什么好处

        (1)易于调度。

        (2)提高并发性。通过线程可方便有效地实现并发性。进程可创建多个线程来执行同一程序的不同部分。

        (3)开销少。创建线程比创建进程要快,所需开销很少。。

        (4)利于充分发挥多处理器的功能。 

    • 相比进程,线程有什么缺点

        (1)线程之间的同步和加锁控制比较麻烦

        (2)一个线程的崩溃影响到整个程序的稳定性

        (3)线程多了之后,线程本身的调度也是一个麻烦事儿,需要消耗较多的CPU 

    • 分离线程

      • 线程可以是可结合的,或者是可分离的;
      • 可结合的线程能够被其他线程收回其资源和杀死。在被其他线程回收之前,它的存储器资源(例如栈)是没有被释放的,相反一个分离的线程是不能被其他线程回收或杀死的。它的存储器资源在它终止时由系统自动释放
      • 为避免存储器泄漏,每个可结合线程都应该被其他线程显式地收回,要么通过调用pthread_detach函数被分离;(对应于Java就是 Thread.join和Thread.detach )
      • 默认情况下,线程被创建成可结合的。(注意:可结合是一种状态,要调用join方法来进行结合/释放)

    来一个例子:

    package com.company;
    
    
    import static java.lang.Thread.sleep;
    
    class Solution {
    
    }
    
    class MyRunnable implements Runnable {
    
        int x = 5;
        @Override
        public void run() {
            synchronized(this) {
                for (int i = 0; i < 5; i++) {
                    System.out.println("hi " + Thread.currentThread().getName() + ":" + x--);
                }
            }
            System.out.println("here to sleep");
            try {
                sleep(5000);
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
        }
    }
    
    public class Main {
    
        public static void main(String[] args) throws InterruptedException {
    
            MyRunnable mr = new MyRunnable();
            Thread t1 = new Thread(mr, "1");
            Thread t2 = new Thread(mr, "2");
            Thread t3 = new Thread(mr, "3");
    
            t1.start();
            t2.start();
            t3.start();
    
            System.out.println();
    
        }
    
    }

    打印结果:

    hi 1:5
    hi 1:4
    hi 1:3
    hi 1:2
    hi 1:1
    here to sleep
    hi 3:0
    hi 3:-1
    hi 3:-2
    hi 3:-3
    hi 3:-4
    here to sleep
    
    hi 2:-5
    hi 2:-6
    hi 2:-7
    hi 2:-8
    hi 2:-9
    here to sleep

    三个线程,同时sleep了5秒钟,然后整个程序才结束。

    如果synchronized加在函数上,那么是每个线程分别sleep 5秒钟,一共sleep 15秒钟。

    • IPC方式(进程间通信方式)

        (1)管道:半双工;用于父子、兄弟之间。

        (2)命名管道(FIFO)

        (2)消息队列:消息链表存于内核,每个消息队列由消息队列标识符标识;于管道不同的是,消息队列存放在内核中,只有在内核重启时才能删除一个消息队列;消息队列的大小受限制。

        (3)信号量(semophore):常用来处理临界资源的访问同步问题。临界资源:为某一时刻只能由一个进程或线程操作的资源。

        (4)共享内存:可以说是最有用的进程间通信方式,也是最快的IPC形式

        (5)套接字:也可用于不同机器之间。

        (6)信号(Signal)

    • 线程同步方式

        (1)临界区:当多个线程访问一个独占性共享资源时,可以使用临界区对象。拥有临界区的线程可以访问被保护起来的资源或代码段,其他线程若想访问,则被挂起,直到拥有临界区的线程放弃临界区为止。

    (注:Java的synchronized代码段,也勉强可以算作临界区,只是语言标记互斥的实现方式;要访问代码段,需要获得传给synchronized的Object这个对象的锁。注意,每个java对象都隐含有一把锁。 

    Java GC需要的safe point,为了让多个线程都停下来,标记的区域-其他线程不进来,里面的线程出来了,就开始GC- 跟临界区的思想也有一点像)

        (2)互斥量-mutex:互斥对象和临界区对象非常相似,只是其允许在进程间使用,而临界区只限制与同一进程的各个线程之间使用。

        (3)条件变量:一个线程被挂起,直到某件事件发生。

        (4)信号量:当需要一个计数器来限制可以使用某共享资源的线程数目时,可以使用“信号量”对象。CSemaphore类对象保存了对当前访问某一个指定资源的线程的计数值,该计数值是当前还可以使用该资源的线程数目。如果这个计数达到了零,则所有对这个CSemaphore类对象所控制的资源的访问尝试都被放入到一个队列中等待,直到超时或计数值不为零为止。

        (5)事件:允许一个线程在处理完一个任务后,主动唤醒另外一个线程执行任务。

        (6)套接字

    感觉上面这篇文章不错,有一定深度。可以多看看这个博客:http://www.cnblogs.com/qiaoconglovelife/

    再来聊一下Python的线程

    简单地说就是作为可能是仅有的支持多线程的解释型语言(perl的多线程是残疾,PHP没有多线程),Python的多线程是有compromise的,在任意时间只有一个Python解释器在解释Python bytecode。Ruby也是有thread支持的,而且至少Ruby MRI是有GIL的。

     单独开了一篇文章来写,详细内容可以看这篇文章:http://www.cnblogs.com/charlesblc/p/6135819.html 

    看一下Javascript里面的线程

    JavaScript引擎是单线程运行的,浏览器无论在什么时候都只且只有一个线程在运行JavaScript程序.  详细学习见这篇文章:
     
     
  • 相关阅读:
    fedora上部署ASP.NET——(卡带式电脑跑.NET WEB服务器)
    SQL Server 请求失败或服务未及时响应。有关详细信息,请参见事件日志或其它适合的错误日志
    8086CPU的出栈(pop)和入栈(push) 都是以字为单位进行的
    FTP 服务搭建后不能访问问题解决
    指定的 DSN 中,驱动程序和应用程序之间的体系结构不匹配
    Linux 安装MongoDB 并设置防火墙,使用远程客户端访问
    svn Please execute the 'Cleanup' command. 问题解决
    .net 操作MongoDB 基础
    oracle 使用绑定变量极大的提升性能
    尝试加载 Oracle 客户端库时引发 BadImageFormatException。如果在安装 32 位 Oracle 客户端组件的情况下以 64 位模式运行,将出现此问题。
  • 原文地址:https://www.cnblogs.com/charlesblc/p/6135666.html
Copyright © 2011-2022 走看看