计算机操作系统多线程多进程实例,操作系统简介&多进程

1.操作系统

操作系统为程序员操作硬件提供了接口。程序员不需要直接写程序操作硬件,只需要按照一定的规范,把数据提交给操作系统,操作系统回去操作硬盘,CPU和内存

1.1第一代计算机

"特点"

没有操作系统的概念,所有程序都是直接操控硬件

"工作过程"

程序员预约一段时间,在这段时间一个程序员独享计算机,其他人等待

"优点"

程序员在申请的时间内独享资源,可以及时调整自己的程序

"缺点"

浪费资源,一段时间内只有一个人使用

1.2第二代计算机:批处理系统

"工作过程"

多个程序员的代码放在一起进行提交,然后机器顺序计算,得到多个程序员代码的输出

"优点"

批处理代码,不再是一个程序员独享计算机,节省了时间

"缺点"

1.整个流程需要人的参与,把多个程序员的代码在机器之间搬动,进行执行和得到输出

2.计算机仍然是顺序执行的

3.程序员不能独享计算机,不能即使调试得到结果,需要等到大家都提交完成,一起处理后得到结果,有问题不能立刻处理

48321922ee8cbb7d0a51f6367a9809f0.png

1.3第三代计算机:多道程序设计

"解决第二代计算机中需要人参与的问题"

使用SPOOLING技术,就不需要人在中间搬动磁带了

"解决第二代计算机顺序执行的问题"

多道技术

多道技术中的多道指的是多个程序,多道技术的实现是为了解决多个程序竞争或共享一个CPU的有序调度问题,解决方式是多路复用,分为时间上的复用和空间上的复用

"空间上的复用:"将内存分为几部分,每部分放入一个程序,这样,同一时间内存中就有了多个程序

"空间上复用的问题:"程序之间的内存必须是分割开的,这种分割需要在硬件层面操作,由操作系统控制。如果内存彼此不分割,则一个程序可以访问另外一个程序的内存。

首先丧失的是安全性,如qq程序可以访问操作系统的内存,这就拿到了操作系统的权限。

其次丧失的是稳定性,如果qq程序退出,可能会导致操作系统的程序退出。

"时间上的复用:"当一个程序在等待I/O时,或一个程序运行了一段时间,CPU就切换,运行其他程序。

"解决单个程序员不能独享计算机的问题"

"分时操作系统:"多个终端+多道技术,可以多个程序员连接计算机,操作系统采用多道的技术,处理多个程序员的任务。所有的用户以为自己独享了计算机资源。

第三代计算机广泛采用了必须的保护硬件(程序之间的内存彼此隔离后),分时系统才开始流行。

1.4第四代计算机:个人计算机

1.5总结

一:操作系统的作用

1.隐藏复杂的硬件接口,提供良好的抽象接口。

2.管理、调度进程,并且将多个进程多硬件的竞争变得有序。

二:多道技术

1.产生背景:针对单核,实现并发

现在的主机一般是多核,多核都会使用多道技术

有4个CPU,运行在cpu1上的某个程序遇到io阻塞,会等到io结束再重新调度,对被调度到4个cpu中的任意一个,具体由操作系统的调度算法决定。

2.空间上的复用:内存中同时存在多个程序,需要硬件支持,将多个程序的内存空间分隔开来。

3.时间上的复用:复用一个cpu时间片。

如果程序在进行io处理,或一个程序占用cpu较长时间,就会吧进程的状态保存下来,去处理别的程序。下次CPU切换回来,继续上次的位置,继续运行。

2.进程理论

2.1什么是进程

进程是正在运行的代码程序。负责运行程序,执行任务的是CPU。

2.2进程与程序的区别

程序仅仅是一堆代码

进程是程序的运行过程,运行起来的代码会在操作系统中产生一个进程

同一个程序运行两次,是两个进程。

2.3并发与并行

无论是并发还是并行,在用户看来都是'同时'运行的

不管是进程还是线程,都只是一个任务而已,真正做事的是CPU,但同一个CPU在同一时刻只能执行一个任务

"并发":是伪并行,即看起来是同时运行。单个cpu+多道技术可以实现并发

例如:你可能正在学习,但突然有人找你聊天,你可能聊一会,看一会视频,等对方有回复了,你在回复。你就做到了做两件事,看似同时进行,但实际上你是一会学习,一会回复,在同一时间实际只做了一件事

"并行":同时运行,需要具备多个CPU才能实现并行。

单核下,可以使用多道技术,多核下,每个CPU也可以使用多道技术。

例如:有四个CPU,6个任务,在同一时间有四个任务在执行,分别分配给了cpu0,cpu1,cpu2,cpu3

如果cpu0上的任务遇到I/O,cpu0就会处理任务5,这就是单核下的多道技术

如果cpu0上的I/O结束了,那么操作系统会重新进行分配,决定调度给那个CPU是操作系统算法和任务优先级决定的。

2.4进程的创建

"进程创建的四种形式"

1.启动操作系统,会创建一些进程。linux中ps -aux查看进程,windows中tasklist查看

2.一个进程在运行过程中开启子进程。(os.fork,subprocess.Popen都可以开启子进程)

3.windows上用户双击某个软件,Linux中启动某个服务,都可以创建一个进程。

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

"linux与Windows中的进程的创建"

1.Windows中调用CreateProcess来创建进程。

2.Unix中调用fork,fork会创建一个与父进程一模一样的副本

"两者的相同与不同"

相同的是:进程创建后,父进程和子进程有个字不同的地址空间(多道技术要求进程之间内存是相互隔离的),任何一个进程中的数据要和其他进程的数据相互隔离,互不影响。

不同的是:在Unix中,子进程的初始地址空间是父进程的一个副本,子进程中的数据来源于父进程,与父进程中的数据是完全相同的,但windows中子进程与父进程并不是完全相同。

2.5进程的终止

1.程序正常退出

2.报错退出

3.被kill掉

2.6进程的层次结构

无论UNIX还是Windows,进程只有一个父进程,不同的是

1.Unix中所有的进程,都是以init进程为根,组成树形结构。父子进程共同组成一个进程组,这样,当从键盘发出一个信号,该信号会被发送给与键盘相关的进程组中的所有成员。

2.在windows中,没有进程层次的概念,所有的进程地位相同,唯一类似于进程层次的,是在创建进程时,父进程会得到一个特别的令牌(称为句柄),该句柄可以用于控制子进程,但是父进程有权把该句柄传给其他子进程,这样就没有层次了。

2.7进程的状态

0d95c731355d4171c841f63d1c31247b.png

运行中的进程被调度到就绪态的原因

1.进程自身遇到I/O阻塞,便要让出CPU让其他进程去执行,这样就保证了CPU一直爱工作

2.与进程无关,操作系统原因,如果一个进程占用时间过多,或者优先级原因,而调用其他进程去使用CPU

2.8进程并发的实现

进程并发的实现在于,硬件中断一个正在运行的进程,会把该进程的状态信息进行保存,为此,操作系统维护一个表格,即进程表,每个进程占用一个进程表项

该表存放了进程状态的重要信息,这样当CPU再次调用时,能从该进程的上次终端位置继续执行,就好像没有中断过一样。

74286cf562a8d581d6ef81781e0d4156.png

3.开启进程的两种方式

3.1multiprocessing模块介绍

python中的"多线程"是无法利用多核优势的

python中的"多进程"是可以充分的利用多核CPU的资源,可以使用os.cpu_count()来查看cpu数

python中使用multiprocessing模块来开启多个子进程,使用threading来开启多个线程。

multiprocessing模块功能众多:支持子进程、通信和共享数据、执行不同形式的同步,提供了Process,Queue,Pipe,Lock等组件。

"进程与线程不同,进程没有任何共享状态,进程修改的数据,改动的仅限于该进程内。"

3.2process类的介绍

Process([group [, target [, name [, args [, kwargs]]]]])

由该类实例化得到的对象,可用来开启一个子进程

"注意"

1.需要使用关键字的方式来制定参数

2.args指定的是传给target函数的位置参数,是一个元组形式,设置方式args=("a",2)

3.kwargs指定的是传给target函数的位置参数,是一个字典的形式,设置方式kwargs={a:2,b:"test"}

"参数介绍"

group:未使用,为None

target:表示调用对象,即子进程要执行的任务,指向一个函数

args:表示target函数的位置参数,元组形式,args=(1,2,'vita',)

kwargs:表示target函数的位置参数,字典形式,kwargs={'name':'vita','age':18}

name:表示子进程的名称

"方法介绍"

p.start():启动进程,并调用该子进程中的run()方法

p.run():进程启动时运行的方法,正是该方法去调用target指定的函数,自定义的继承自Process的类,需要实现该方法

p.terminate():强制终止进程p,不会做任何的清理工作,

如果p创建了子进程,该子进程就成了僵尸进程

如果p还保存了一个锁,那么锁也不会被释放,进而导致死锁

p.is_alive():如果p在运行,就返回True

p.join([timeout]):主线程等待p终止

"注意"是主线程处于等待状态,而p处于运行状态

"属性介绍"

p.daemon:默认为False,

如果设为True,代表p为后台运行的守护进程,当p的父进程终止时,p也随之终止

设置为True后,p不能创建自己的新进程,必须在p.start之后设置

p.name:进程的名称

p.pid:进程的pid

3.3Process创建子进程

注意:在windows中Process()必须放到# if __name__ == '__main__':下

"方式一"

#!/usr/bin/env python

# -*- coding:utf-8 -*-

# Author: vita

from multiprocessing import Process

def target_func(name):

print("子进程:",name)

if __name__ == '__main__':

# 实例化两个对象,开启两个子进程

p1=Process(name="p1",target=target_func,args=("p1",))# 这里一定要加',',否则TypeError: target_func() takes 1 positional argument but 2 were given

p2=Process(name="p2",target=target_func,args=("p2",))

p1.start()

p2.start()

print("主进程") #会发现创建了子进程,主进程依旧在继续运行,所以先输出了主进程的内容,然后运行子进程,

D:\software2\Python3\install\python.exe E:/PythonProject/new-python/python-test/BasicGrammer/test.py

主进程

子进程: p1

子进程: p2

Process finished with exit code 0

"方式二"

#!/usr/bin/env python

# -*- coding:utf-8 -*-

# Author: vita

from multiprocessing import Process

# 这里要继承Process

class Func(Process):

# 重用父类中的init方法,并加入新参数

def __init__(self,name):

super().__init__()

self.nm=name

# 一定要写run 方法,start方法会自动调用run方法

def run(self): #

print("子进程",self.nm)

if __name__ == '__main__':

# 实例化两个对象,开启两个子进程

func = Func("p1")

func.start()

func1 = Func("p2")

func1.start()

print("主进程")

D:\software2\Python3\install\python.exe E:/PythonProject/new-python/python-test/BasicGrammer/test.py

主进程

子进程 p1

子进程 p2

Process finished with exit code 0

3.4pid和ppid

#!/usr/bin/env python

# -*- coding:utf-8 -*-

# Author: vita

from multiprocessing import Process

import os

# 这里要继承Process

class Func(Process):

def __init__(self,name):

super().__init__()

self.nm=name

def run(self): #

# 子进程的ppid就是main的pid

print("子进程:%s,pid:%s,ppid:%s"%(self.nm,os.getpid(),os.getppid()))

if __name__ == '__main__':

func = Func("p1")

func.start()

func1 = Func("p2")

func1.start()

# 主进程的ppid就是你运行py文件的程序的进程号,

# 如果使用pycharm运行,就是pycharm的进程号

# 如果使用cmd终端运行,就是终端的进程号

print("主进程:,pid:%s,ppid:%s"%(os.getpid(),os.getppid()))

D:\software2\Python3\install\python.exe E:/PythonProject/new-python/python-test/BasicGrammer/test.py

主进程:,pid:6100,ppid:11200

子进程:p1,pid:8252,ppid:6100

子进程:p2,pid:10784,ppid:6100

Process finished with exit code 0

4.Process方法和属性详解

4.1join()

在主进程运行过程中,可以开启子进程,主进程与子进程的任务分为

1.在主进程的任务与子进程的任务彼此独立时,主进程先执行完成,还要等待子进程执行完成后才统一回收资源

2.如果主进程的任务需要在执行到某个阶段,需要等待子进程执行完成后继续执行,就可使用join()

"join"

#!/usr/bin/env python

# -*- coding:utf-8 -*-

# Author: vita

from multiprocessing import Process

import os

import time

# 这里要继承Process

class Func(Process):

def __init__(self,name):

super().__init__()

self.nm=name

def run(self): #

print("子进程:%s is running"%(self.nm))

time.sleep(3)

print("子进程:%s is finishing" % (self.nm))

if __name__ == '__main__':

p_list=[]

p1 = Func("p1")

# 只是发送信号给操作系统,治愈操作系统何时执行,还要看系统内部的调用

p1.start()

p_list.append(p1)

p2 = Func("p2")

p2.start()

p_list.append(p2)

# 方法哦一个列表中,for循环来执行join

for p in p_list:

p.join()

print("主进程运行完成")

D:\software2\Python3\install\python.exe E:/PythonProject/new-python/python-test/BasicGrammer/test.py

子进程:p1 is running

子进程:p2 is running

子进程:p1 is finishing

子进程:p2 is finishing

主进程运行完成

Process finished with exit code 0

进程执行了start()后,信号就已经发送给了操作系统,操作系统会根据自己的调度规则,进行运行

join()是让主进程等待多个子进程的运行,此时所有的子进程还是并发执行的,

所有的子进程的运行时间是运行时间最长的子进程的运行时间。当所有子进程运行完成后,继续执行主进程的代码

4.2其他属性和方法

"p.terminate(),p.is_alive,p.name,p.pid"

#!/usr/bin/env python

# -*- coding:utf-8 -*-

# Author: vita

from multiprocessing import Process

import os

import time

# 这里要继承Process

class Func(Process):

def __init__(self,name):

super().__init__()

self.nm=name

def run(self): #

print("子进程:%s is running"%(self.nm))

time.sleep(3)

print("子进程:%s is finishing" % (self.nm))

if __name__ == '__main__':

p1 = Func("p1")

# 只是发送信号给操作系统,至于操作系统何时执行,还要看系统内部的调用

p1.start()

# 执行了terminate(),就没有执行子进程的代码了

p1.terminate()

# 执行terminate()也是发送给操作系统一个进程终止信号,但不会立刻终止

print(p1.is_alive())

print("主进程")

print("子进程名:%s,子进程pid:%s" %(p1.name,p1.pid))

time.sleep(2)

# 休息2秒后,才终止

print(p1.is_alive())

D:\software2\Python3\install\python.exe E:/PythonProject/new-python/python-test/BasicGrammer/test.py

True

主进程

子进程名:Func-1,子进程pid:14768

False

Process finished with exit code 0

5.练习

5.1思考开启进程的方式一和方式二各开启了几个进程?

总共有主进程+开启的子进程

5.2进程之间的内存空间是共享的还是隔离的?下述代码的执行结果是什么?

"进程间的数据是相互隔离的,所以主进程仍然输出100,子进程是0"

#!/usr/bin/env python

# -*- coding:utf-8 -*-

# Author: vita

from multiprocessing import Process

def work():

global n

n=0

print('子进程内: ',n)

if __name__ == '__main__':

n = 100 # 在windows系统中应该把全局变量定义在if __name__ == '__main__'之上就可以了

p=Process(target=work)

p.start()

p.join()

print('主进程内: ',n)

D:\software2\Python3\install\python.exe E:/PythonProject/new-python/python-test/BasicGrammer/test.py

子进程内: 0

主进程内: 100

Process finished with exit code 0

5.3基于多进程实现并发的套接字通信?

"server"

#!/usr/bin/env python

# -*- coding:utf-8 -*-

# Author: vita

from socket import *

from multiprocessing import Process

def talk(conn):

while True:

try:

data = conn.recv(1024)

if not data: break

conn.send(data.upper())

except ConnectionResetError:

break

conn.close()

def server(ip,port):

server = socket(AF_INET, SOCK_STREAM)

server.setsockopt(SOL_SOCKET, SO_REUSEADDR, 1)

server.bind((ip,port))

server.listen(5)

while True:

conn, addr = server.accept()

p = Process(target=talk, args=(conn,))

p.start()

server.close()

if __name__ == '__main__':

server('127.0.0.1', 8080)

"client"

#!/usr/bin/env python

# -*- coding:utf-8 -*-

# Author: vita

from socket import *

client=socket(AF_INET,SOCK_STREAM)

client.connect(('127.0.0.1',8080))

while True:

msg=input('>>: ').strip()

if not msg:continue

client.send(msg.encode('utf-8'))

data=client.recv(1024)

print(data.decode('utf-8'))

5.4思考每来一个客户端,服务端就开启一个新的进程来服务它,这种实现方式有无问题?

如果客户端过多,服务端就会开启很多个子进程,会耗尽资源

5.5改写下列程序,分别别实现下述打印效果

1、改写下列程序,分别别实现下述打印效果

from multiprocessing import Process

import time

import random

def task(n):

time.sleep(random.randint(1,3))

print('-------->%s' %n)

if __name__ == '__main__':

p1=Process(target=task,args=(1,))

p2=Process(target=task,args=(2,))

p3=Process(target=task,args=(3,))

p1.start()

p2.start()

p3.start()

print('-------->4')

效果一:保证最先输出-------->4

"上面的代码不做更改,就是首先输出4"

-------->4

-------->1

-------->3

-------->2

效果二:保证最后输出-------->4

"p1.join()

p2.join()

p3.join()

这样就能让主进程等待子进程执行完成后,继续执行主进程,就可最后输出4"

-------->2

-------->3

-------->1

-------->4

效果三:保证按顺序输出

" p1.start()

p1.join()

p2.start()

p2.join()

p3.start()

p3.join()"

-------->1

-------->2

-------->3

-------->4

2、判断上述三种效果,哪种属于并发,哪种属于串行?

"前两种是并发执行,最后一个是串行"

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值