python并发编程

1、进程的概念及相关知识

	第一,进程是一个实体。每一个进程都有它自己的地址空间,一般情况下,包括文本区域(text region)、数据区域(data region)和堆栈(stack region)
。文本区域存储处理器执行的代码;数据区域存储变量和进程执行期间使用的动态分配的内存;堆栈区域存储着活动过程调用的指令和本地变量。
	第二,进程是一个“执行中的程序”。程序是一个没有生命的实体,只有处理器赋予程序生命时(操作系统执行之),它才能成为一个活动的实体,我们称其为进程。[3] 
进程是操作系统中最基本、重要的概念。是多道程序系统出现后,为了刻画系统内部出现的动态情况,描述系统内部各道程序的活动规律引进的一个概念,所有多道程序设计操作系统都建立在进程的基础上。
2.进程的特征

动态性:进程的实质是程序在多道程序系统中的一次执行过程,进程是动态产生,动态消亡的。
并发性:任何进程都可以同其他进程一起并发执行
独立性:进程是一个能独立运行的基本单位,同时也是系统分配资源和调度的独立单位;
异步性:由于进程间的相互制约,使进程具有执行的间断性,即进程按各自独立的、不可预知的速度向前推进
结构特征:进程由程序、数据和进程控制块三部分组成。
多个不同的进程可以包含相同的程序:一个程序在不同的数据集里就构成不同的进程,能得到不同的结果;但是执行过程中,程序不能发生改变。
3.进程与程序中的区别

程序是指令和数据的有序集合,其本身没有任何运行的含义,是一个静态的概念。
而进程是程序在处理机上的一次执行过程,它是一个动态的概念。
程序可以作为一种软件资料长期存在,而进程是有一定生命期的。
程序是永久的,进程是暂时的。
4、并行与并发
并行 : 并行是指两者同时执行,比如赛跑,两个人都在不停的往前跑;(资源够用,比如三个线程,四核的CPU )
并发 : 并发是指资源有限的情况下,两者交替轮流使用资源,比如一段路(单核CPU资源)同时只能过一个人,A走一段后,让给B,B用完继续给A ,交替使用,目的是提高
区别:
并行是从微观上,也就是在一个精确的时间片刻,有不同的程序在执行,这就要求必须有多个处理器。
并发是从宏观上,在一个时间段上可以看出是同时执行的,比如一个服务器同时处理多个
5、同步异步 阻塞非阻塞

同步和异步

    所谓同步就是一个任务的完成需要依赖另外一个任务时,只有等待被依赖的任务完成后,依赖的任务才能算完成,这是一种可靠的任务序列。要么成功都成功,失败都失败,两个任务的状态可以保持一致。

   所谓异步是不需要等待被依赖的任务完成,只是通知被依赖的任务要完成什么工作,依赖的任务也立即执行,只要自己完成了整个任务就算完成了。至于被依赖的任务最终是否真正完成,依赖它的任务无法确定,
    所以它是不可靠的任务序列
比如我去银行办理业务,可能会有两种方式:
第一种 :选择排队等候;
第二种 :选择取一个小纸条上面有我的号码,等到排到我这一号时由柜台的人通知我轮到我去办理业务了;

第一种:前者(排队等候)就是同步等待消息通知,也就是我要一直在等待银行办理业务情况;

第二种:后者(等待别人通知)就是异步等待消息通知。在异步消息处理中,等待消息通知者(在这个例子中就是等待办理业务的人)往往注册一个回调机制,
在所等待的事件被触发时由触发机制(在这里是柜台的人)通过某种机制(在这里是写在小纸条上的号码,喊号)找到等待该事件的人。
阻塞与非阻塞

      阻塞和非阻塞这两个概念与程序(线程)等待消息通知(无所谓同步或者异步)时的状态有关。
也就是说阻塞与非阻塞主要是程序(线程)等待消息通知时的状态角度来说的
继续上面的那个例子,不论是排队还是使用号码等待通知,如果在这个等待的过程中,等待者除了等待消息通知之外不能做其它的事情,那么该机制就是阻塞的,表现在程序中,也就是该程序
一直阻塞在该函数调用处不能继续往下执行。
相反,有的人喜欢在银行办理这些业务的时候一边打打电话发发短信一边等待,这样的状态就是非阻塞的,因为他(等待者)没有阻塞在这个消息通知上,而是一边做自己的事情一边等待。

注意:同步非阻塞形式实际上是效率低下的,想象一下你一边打着电话一边还需要抬头看到底队伍排到你了没有。如果把打电话和观察排队的位置看成是程序的两个操作的话,
这个程序需要在这两种不同的行为之间来回的切换,效率可想而知是低下的;而异步非阻塞形式却没有这样的问题,因为打电话是你(等待者)的事情,而通知你则是柜台(消息触发机制)的事情,程序没有在两种不同的操作中来回切换。


进程的创建和结束
进程的创建

  但凡是硬件,都需要有操作系统去管理,只要有操作系统,就有进程的概念,就需要有创建进程的方式,一些操作系统只为一个应用程序设计,比如微波炉中的控制器,一旦启动微波炉,所有的进程都已经存在。

  而对于通用系统(跑很多应用程序),需要有系统运行过程中创建或撤销进程的能力,主要分为4中形式创建新的进程:

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

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

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

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

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

2、python process 模块介绍

参数说明
Process([group [, target [, name [, args [, kwargs]]]]]),由该类实例化得到的对象,表示一个子进程中的任务(尚未启动)
强调:
1. 需要使用关键字的方式来指定参数
2. args指定的为传给target函数的位置参数,是一个元组形式,必须有逗号

参数介绍:
group参数未使用,值始终为None
target表示调用对象,即子进程要执行的任务
args表示调用对象的位置参数元组,args=(1,2,'egon',)
kwargs表示调用对象的字典,kwargs={'name':'egon','age':18}
name为子进程的名称


方法介绍
p.start():启动进程,并调用该子进程中的p.run() 
p.run():进程启动时运行的方法,正是它去调用target指定的函数,我们自定义类的类中一定要实现该方法  
p.terminate():强制终止进程p,不会进行任何清理操作,如果p创建了子进程,该子进程就成了僵尸进程,使用该方法需要特别小心这种情况。如果p还保存了一个锁那么也将不会被释放,
进而导致死锁
p.is_alive():如果p仍然运行,返回True
p.join([timeout]):主线程等待p终止(强调:是主线程处于等的状态,而p是处于运行的状态)。timeout是可选的超时时间,需要强调的是,p.join只能join住start开启的进程,
而不能join住run开启的进程 


属性介绍
p.daemon:默认值为False,如果设为True,代表p为后台运行的守护进程,当p的父进程终止时,p也随之终止,并且设定为True后,p不能创建自己的新进程,必须在p.start()之前设置
p.name:进程的名称
p.pid:进程的pid
p.exitcode:进程在运行时为None、如果为–N,表示被信号N结束(了解即可)
p.authkey:进程的身份验证键,默认是由os.urandom()随机生成的32字符的字符串。这个键的用途是为涉及网络连接的底层进程间通信提供安全性,这类连接只有在具有相同的身份验证键时
才能成功(了解即可)


在windows中使用process模块的注意事项
在Windows操作系统中由于没有fork(linux操作系统中创建进程的机制),在创建子进程的时候会自动 import 启动它的这个文件,而在 import 
的时候又执行了整个文件。因此如果将process()直接写在文件中就会无限递归创建子进程报错。所以必须把创建子进程的部分使用if __name__ ==‘__main__’ 
判断保护起来,import 的时候  ,
就不会递归运行了。
```yaml
进程的创建和使用:
from  multiprocessing import Process

def run():
    print("22222")
if __name__=="__main__":
    p=Process(target=run)      #  实例化进程
    p.start()


print("*******************************************")

# 进程传参数
def run(*args):
    print(args)
if __name__=="__main__":
    p=Process(target=run,args=(10,20,30))      #  实例化进程
    p.start()

print("*******************************************")

# 来查看进程直间的关系   os.getpid() 查看当前进程号    os.getppid() 查看当前父进程号  
def run(*args):
    print(args)
    time.sleep(1)
    print(1111111111111111111111111111)
    print(os.getpid(),"查看当前子进程号3")       #    # os.getpid() 查看当前进程号
    print(os.getppid(),"查看父进程号4")          #    os.getppid() 查看当前父进程号

if __name__ == "__main__":
    p1 = Process(target=run, args=(10, 20, 30))  # 实例化进程
    p1.start()
    print("我是主进程。。。。。")
    print(os.getpid(),"查看当前主进程号1")          #    # os.getpid() 查看当前进程号
    print(os.getppid(),"查看当前执行父进程号2")     #    #   os.getppid() 查看当前父进程号
    print(p1.name)                                 # Process-1

print("*******************************************")

# 多进程
"""multiprocessing 库
是一个跨平台版本多进程模块 提供了一个Process类来代表一个进程对象
"""
from multiprocessing import Process
from time import sleep
import os
# 子进程需要执行代码
def run(str):
    print("子进程启动了")
    sleep(3)
    print("子进程结束")

if __name__ == "__main__":
    print("main是主(父)进程启动....")
    # 01 创建进程 子进程
    p=Process(target=run,args =("nice",))  #    target 说明进程执行的指定代码(函数)
    # 02  启动进程
    # 父进程的结束不能影响子进程 让父进程等待子进程结束在执行父进程
    p.start()
    print("父进程结束....")
    sleep(2)

# 执行顺序
#     main是主(父)进程启动....
#     父进程结束....
#     子进程启动了
#     子进程结束
from multiprocessing import Process


def f(name):
    print('hello', name)
    print('我是子进程')

if __name__ == '__main__':
    p = Process(target=f, args=('bob',))
    p.start()  
    print('执行主进程的内容了')

######输出内容如下:
执行主进程的内容了
hello bob
我是子进程

###思考先执行了父进程在执行了子进程,思考:p.start() 执行的 print('执行主进程的内容了') 后执行的为什么先打印出了执行主进程的内容了??????
原因 在执行上述代码中,从上往下执行,但是执行f函数时候是放入了另外一个进程中,所有就先打印出了父进程。

例二:

import time
from multiprocessing import Process

def f(name):
    print('hello', name)
    time.sleep(1)
    print('我是子进程')


if __name__ == '__main__':
    p = Process(target=f, args=('bob',))
    p.start()
    p.join()   ###阻塞
    print('我是父进程')

##执行结果如下:
hello bob
我是子进程
我是父进程

##join 是阻塞等待子进程执行完毕,及 先执行子进程的操作,执行完成在执行父进程。

############################线程讲解开始#######################

1、线程介绍

同一个进程之间多个线程内存是共享的
线程的开启速度比进程快

线程和进程之间的区别:

  地址空间和其它资源(如打开文件):进程间相互独立,同一进程的各线程间共享。某进程内的线程在其它进程不可见。

  通信:进程间通信IPC,线程间可以直接读写进程数据段(如全局变量)来进行通信——需要进程同步和互斥手段的辅助,以保证数据的一致性。

  调度和切换:线程上下文切换比进程上下文切换要快得多。

  在多线程操作系统中,进程不是一个可执行的实体。

大部分主要涉及文件\网络的操作,多线程会更快

2、线程特点

在多线程的操作系统中,通常是在一个进程中包括多个线程,每个线程都是作为利用CPU的基本单位,是花费最小开销的实体。线程具有以下属性。
  1)轻型实体
  线程中的实体基本上不拥有系统资源,只是有一点必不可少的、能保证独立运行的资源。
  线程的实体包括程序、数据和TCB。线程是动态概念,它的动态特性由线程控制块TCB(Thread Control Block)描述。
  2)独立调度和分派的基本单位。
  在多线程OS中,线程是能独立运行的基本单位,因而也是独立调度和分派的基本单位。由于线程很“轻”,故线程的切换非常迅速且开销小(在同一进程中的)。
  3)共享进程资源。
  线程在同一进程中的各个线程,都可以共享该进程所拥有的资源,这首先表现在:所有线程都具有相同的进程id,这意味着,线程可以访问该进程的每一个内存资源;此外,
还可以访问进程所拥有的已打开文件、定时器、信号量机构等。由于同一个进程内的线程共享内存和文件,所以线程之间互相通信不必调用内核。
  4)可并发执行。
  在一个进程中的多个线程之间,可以并发执行,甚至允许在一个进程中所有线程都能并发执行;同样,不同进程中的线程也能并发执行,充分利用和发挥了处理机与外围设备并行工作的能力

3、线程的创建

from threading import Thread
import time

def san(name):
    time.sleep(2)
    print('我是子线程')
if __name__ =='__main__':
    t = Thread(target=san,args=('你猜',))
    t.start()
    print('我是父线程')

#使用类来启动多线程
class Mythread(Thread):
    def __init__(self,aa):
        super().__init__()
        self.aa=aa
    def run(self):
        print('ceshi')
        print(self.aa)
    def run1(self):
        print('ceshi2')
p = Mythread('子线程')
p.start()
p.run1()
## 多线程请求不同网址

import requests
import time
url_lst = [
    'http://www.baidu.com',
    'http://www.xueersi.com',
    'http://www.jiayuan.com',
    'http://www.mi.com'
]
start_t = time.time()
for url in url_lst:
    ret = requests.get(url)
print(time.time()-start_t)

#上述是常规的请求方式  下面采用多线程的方式请求

from threading import Thread
def get_url(url):
    ret = requests.get(url)   ###请求一个网页
start_t = time.time()
t_lst = []
for url  in url_lst:            #开启是个线程,每个线程去请求一个网页,即同时请求十个网页
    t = Thread(target=get_url,args=(url,))
    t.start()
    t_lst.append(t)
for t in t_lst:
    t.join()
print(time.time()-start_t)

###以上是多个线程去请求不同的网址
  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值