Java教程

并发编程

本文主要是介绍并发编程,对大家解决编程问题具有一定的参考价值,需要的程序猿们随着小编来一起学习吧!

一、操作系统的发展史

    手工操作系统

       人们先把程序纸带(或卡片)装上计算机,然后启动输入机把程序和送入计算机,接着通过控制台开关启动程序运行。计算完毕,打印机输出计算结果,用户卸下并取走纸带(或卡片)。第二个用户上机,重复同样的步骤。

          缺点: 用户独占机器,CPU等待手工操作,CPU利用不充分。

        由于手工操作的满速度和计算机的高速度之间形成了尖锐矛盾,手工操作的方式是计算机的资源利用率极低,唯一的解决办法只有摆脱手工操作,实现作业的自动过渡。

      

 

 

 

    联机批处理系统

       在主机和输入机之间增加两个存储设备——磁带机,在监督程序的自动控制下,计算机自动完成任务。

            成批的把输入机上的用户作业读入磁带,依次把磁带上的用户作业读入主机内存并执行,执行完成后把计算结果想输出机输出。 完成一批作业后,监督程度又从输入机读取作业存入磁带机。按照上面的步骤重复处理任务。监督程序不停的处理各个作业,实现了作业的自动转接,减少了作业的建立时间和手工操作时间,有效的克服了人机矛盾,提高了计算机资源的利用率。              

            缺点: 在输入作业和输出结果时,CPU还是会处于线空闲状态,等待慢速的输入/输出设备完成工作——主机处于忙等状态。

      

 

 

     脱机批处理系统

        为了克服与缓解告诉主机与慢速外设(输入输出设备),提高CPU利用率,用又引入了脱机批处理系统,即输入输出脱离主机控制。

             显著特征就是:增加一台不与主机直接相连卫星机。卫星机用来从输入机上读取用户作业并放到磁带机上;将磁带机上的执行结果传给输出机。这样主机不再与慢速的输入输出设备连接。主机与卫星机两者并行工作,分工明确,可充分发挥主机的告诉计算能力。

          缺点:每次主机内存中仅存放一道作业,每当它运行期间发出输入/输出(I/O)请求后,高速的CPU便处于等待低速的I/O完成状态,致使CPU空闲。

 

 

     多道程序系统与分时系统

        为了解决脱机批处理系统CPU空闲的问题,于是又有了多道程序系统,多道程序设计技术,就是指允许多个程序同时进入内存并运行。即同时把多个程序放入内存,并允许它们交替在CPU中运行,它们共享系统中的各种硬、软件资源。当一道程序因I/O请求而暂停运行时,CPU便立即转去运行另一道程序,当某个程序占用CPU时间过久时,操作系统也会剥夺该CPU的执行权限。

        缺点: 如果有一个程序占用CPU的时间很长,而它后面的其他程序只需要占用CPU一点时间,那后面的程序也必须等前面的程序执行完后才会轮到后面的程序,这样就会造成资源效率低下的情况。

        于是就又有了一个分时系统, 分时技术:把处理机的运行时间分成很短的时间片,按时间片轮流把处理机分配给各联机作业使用。若某个作业在分配给它的时间片内不能完成其计算,则该作业暂时中断,把处理机让给另一作业使用,等待下一轮时再继续其运行。由于计算机速度很快,作业运行轮转得很快,给每个用户的印象是,好象他独占了一台计算机。而每个用户可以通过自己的终端向系统发出各种操作控制命令,在充分的人机交互情况下,完成作业的运行。具有上述特征的计算机系统称为分时系统,它允许多个用户同时联机使用计算机。

 

 

二、进程

    什么是进程?

      进程就是正在运行的程序,程序就是一堆代码。

    进程的调度算法

      ① 先来先服务算法:如名字所说,将先进来的程序先运行完再运行后面的程序。

        缺点:如果在前面的CPU执行的时间长,后面的CPU执行时间很短,会造成资源效率低下的情况

      ② 短作业优先调度算法:将短作业或者短进程的程序优先执行

        缺点:长作业的进程会放在最后面,也会造成资源效率低下的情况

      ③ 时间片轮转法+多级反馈队列:将一段时间分成多个时间片,一一分配给各个进程执行,在进入到下一个阶级时,如果某些程序还存在的话,会分配更多时间片给该层级的程序,按照此种情况,越往下的层级分配到的时间片也就越多。

    进程状态介绍

        1.创建状态
          进程由创建而产生。创建进程是一个非常复杂的过程,一般需要通过多个步骤才能完成:如首先由进程申请一个空白的进程控制块(PCB),并向PCB中填写用于控制和管理进程的信息;然后为该进程分配运行时所必须的资源;最后,把该进程转入就绪状态并插入到就绪队列中。

        2.就绪状态
        这是指进程已经准备好运行的状态,即进程已分配到除CPU以外所有的必要资源后,只要再获得CPU,便可立即执行。如果系统中有许多处于就绪状态的进程,通常将它们按照一定的策略排成一个队列,该队列称为就绪队列。有执行资格,没有执行权的进程。

        3.运行状态
        这里指进程已经获取CPU,其进程处于正在执行的状态。对任何一个时刻而言,在单处理机的系统中,只有一个进程处于执行状态而在多处理机系统中,有多个进程处于执行状态。既有执行资格,又有执行权的进程。

        4.阻塞状态
        这里是指正在执行的进程由于发生某事件(如I/O请求、申请缓冲区失败等)暂时无法继续执行的状态,即进程执行受到阻塞。此时引起进程调度,操作系统把处理机分配给另外一个就绪的进程,而让受阻的进程处于暂停的状态,一般将这个暂停状态称为阻塞状态

        

        5.终止状态
        进程的终止也要通过两个步骤:首先,是等待操作系统进行善后处理,最后将其PCB清零,并将PCB空间返还给系统。当一个进程到达了自然结束点,或是出现了无法克服的错误,或是被操作系统所终结,或是被其他有终止权的进程所终结,它将进入终止状态。进入终止态的进程以后不能在再执行,但是操作系统中任然保留了一个记录,其中保存状态码和一些计时统计数据,供其他进程进行收集。一旦其他进程完成了对其信息的提取之后,操作系统将删除其进程,即将其PCB清零,并将该空白的PCB返回给系统。

 

 

 

    进程的并行与并发

       并行:在同一时间同时执行多个程序称为并行

       并发:在一段时间内,A程序与B程序一起分配CPU的资源,看起来像是同时运行的

    同步与异步

       同步:调用某个方法时,必须等到返回结果才会执行下一步操作。

 

 

 

       异步:调用某个方法时,可以直接执行下一步操作,结果会由反馈机制自动提醒。

 

 

 

    阻塞与非阻塞

      阻塞:当程序遇到读或者写和睡眠状态时,会进入阻塞状态。

      

    同步异步与阻塞非阻塞的区别

      阻塞和非阻塞 强调的是程序在等待调用结果(消息,返回值)时的状态.  阻塞调用是指调用结果返回之前,当前线程会被挂起。调用线程只有在得到结果之后才会返回。非阻塞调用指在不能立刻得到结果之前,该调用不会阻塞当前线程。 对于同步调用来说,很多时候当前线程还是激活的状态,只是从逻辑上当前函数没有返回而已,即同步等待时什么都不干,白白占用着资源。

      同步和异步强调的是消息通信机制 (synchronous communication/ asynchronous communication)。所谓同步,就是在发出一个"调用"时,在没有得到结果之前,该“调用”就不返回。但是一旦调用返回,就得到返回值了。换句话说,就是由“调用者”主动等待这个“调用”的结果。而异步则是相反,"调用"在发出之后,这个调用就直接返回了,所以没有返回结果。换句话说,当一个异步过程调用发出后,调用者不会立刻得到结果。而是在"调用"发出后,"被调用者"通过状态、通知来通知调用者,或通过回调函数处理这个调用

 

 

三、创建进程的方式

  

  1. 系统初始化(查看进程linux中用ps命令,windows中用任务管理器,前台进程负责与用户交互,后台运行的进程与用户无关,运行在后台并且只在需要时才唤醒的进程,称为守护进程,如电子邮件、web页面、新闻、打印)

  2. 一个进程在运行过程中开启了子进程(如nginx开启多进程,os.fork,subprocess.Popen等)

  3. 用户的交互式请求,而创建一个新进程(如用户双击暴风影音)

  4. 一个批处理作业的初始化(只在大型机的批处理系统中应用)

  无论哪一种,新进程的创建都是由一个已经存在的进程执行了一个用于创建进程的系统调用而创建的。 

    在python中创建进程

      需要使用到multiprocessing模块

from multiprocessing import Process
import os

def test():
    print('from 创建的子进程')
    print(os.getpid()) # 获取进程的pid
    print(os.getppid()) # 获取父进程的pid

if __name__ == '__main__': # 在Windows系统中创建子进程必须加这个
    p = Process(target=test) # 创建子进程
    p.start()  # 执行子进程
    print('父进程')
    print(os.getpid()) # 获取进程的pid

 

 

    join方法(使子进程执行完毕后才执行父进程)

from multiprocessing import Process
import os

def test():
    print('from 创建的子进程')
    print(os.getpid()) # 获取进程的pid
    print(os.getppid()) # 获取父进程的pid

if __name__ == '__main__': # 在Windows系统中创建子进程必须加这个
    p = Process(target=test)
    p.start()
    p.join() # 必须放在父进程要执行的代码前
    print('父进程')
    print(os.getpid())

 

 

    小练习1 求以下代码运行时间

from multiprocessing import Process
import time


def test(name, ):
    print('%s is running' % name)
    time.sleep(3)
    print('%s is over' % name)


if __name__ == '__main__':
    p_list = []
    start_time = time.time()
    for i in range(1, 4):
        p = Process(target=test, args=(i, ))
        p.start()
        p_list.append(p)
        p.join()  # 
    print(time.time() - start_time)

 

 

     小练习2 求以下代码运行时间

from multiprocessing import Process
import time


def test(name, ):
    print('%s is running' % name)
    time.sleep(3)
    print('%s is over' % name)


if __name__ == '__main__':
    p_list = []
    start_time = time.time()
    for i in range(1, 4):
        p = Process(target=test, args=(i, ))
        p.start()
        p_list.append(p)
    for p in p_list:
        p.join()
    print(time.time() - start_time)

 

 

    进程之间默认是互相不影响的

a=1
def test():
    global a # 修改全局变量
    a = 10


if __name__ == '__main__':
    p = Process(target=test)
    p.start()
    p.join()
    print(a)
   

 

     多进程中的其他方法

      ① current_process查看进程号 (需要导入模块from multiprocessing current )

      ② os.getpid() 查看进程号  

      ③ Process.name 查看进程的名字

      ④ Process.terminate() 杀死子进程

      ⑤ Process.is_alive() 判断子进程是否存货

这篇关于并发编程的文章就介绍到这儿,希望我们推荐的文章对大家有所帮助,也希望大家多多支持为之网!