并发编程(进程,线程和协程)

目录

进程

 同步与异步

join方法

守护进程daemon方法:

线程

线程的GIL锁

同步锁

线程池

协程asyncio


进程和线程相关方法都为内置模块或内置方法,可引入包后直接调用

进程 就好比工厂的车间,它代表CPU所能处理的单个任务

线程 就好比车间里的工人。一个进程可以包括多个线程

进程

广义定义进程是一个具有一定独立功能的程序关于某个数据集合的一次运行活动。它是操作系统动态执行的基本单元,在传统的操作系统中,进程既是基本的分配单元,也是基本的执行单元。

在操作系统中,每启动一个应用程序其实就是OS开启了一个进程且为进程分类对应的内存/资源,应用程序的执行也就是进程在执行。

并发与并行

通过进程之间的调度,也就是进程之间的切换,无论是并行还是并发,在用户看来都是'同时'运行的,不管是进程还是线程,都只是一个任务而已,真实干活的是cpu,而一个cpu同一时刻只能执行一个任务。并行与并发区别在于是否同时进行

并行:同时运行,只有具备多个cpu才能实现并行

并发:是伪并行,多任务交替执行

进程的状态

程序会进入几个状态:就绪,运行、阻塞和终止。

就绪(Ready)状态进程已经准备好,已分配到所需资源/内存。

执行/运行(Running)状态进程处于就绪状态被调度后,进程进入执行状态

阻塞(Blocked)状态正在执行的进程由于某些事件(I/O请求,input,申请缓存区失败)而暂时无法运行,进程受到阻塞,则进入就绪状态等待系统调用

终止状态进程结束,或出现错误,或被系统终止,进入终止状态。无法再执行

 同步与异步

同步方法调用一旦开始,调用者必须等到方法调用返回后,才能继续该方法后续的行为代码。

异步方法调用更像一个消息传递,一旦调用开始,该方法调用就会立即返回,调用者就可以继续后续的操作。而异步方法通常会在另外一个线程/进程中,“真实”地执行着。整个过程,不会阻碍调用者的工作

注:同步和异步针对是cpu遇到阻塞操作时,所产生的不同行为,阻塞时同步方法暂停而异步算法执行其他线程

python进程的实现

multiprocessing包multiprocess是python中管理进程的包。 之所以叫multi是取自multiple的多功能的意思,在这个包中几乎包含了和进程有关的所有子模块,提供的子模块非常多。

Process模块Process模块是一个创建进程的模块,借助这个模块,就可以完成进程的创建。

之前我们说过,运行一个py文件就相当于启动了一个进程,这个进程我们成为"主进程"而在主进程对应的py文件中,可以通过Process模块创建另一个进程,这个进程是基于主进程创建的,因此可以被称为"子进程"

 当有了两个进程后,我们其实就可以实现异步机制了!

1.导入模块:from multiprocessing import Process

2.基于Process创建一个子进程对象(当前运行的整个py文件表示主进程),然后可以基于target参数将外部的一个函数注册到该子进程中

3.基于start()方法启动创建好的子进程

#自定义的一组任务
def get_request(url):
    print('正在准备请求网址:',url)
    sleep(2)
    print('数据请求成功:',url)


if __name__ == '__main__':
    print('我是主进程程序!')

    p = Process(target=get_request,args=("wwww.123.com",))    #创建一个子进程对象
                                                              并传参:函数和普通参数
    p.start()   #执行子进程

join方法

如果有时候,主进程需要使用子进程运行后的结果则必须保证主进程等待子进程运行结束后在结束,如何实现呢?

主进程会在加上join的地方等待(也就是阻塞住),会等待子进程执行完之后,再继续往后执行主进程join后序的部分

from multiprocessing import Process
from time import sleep

totalTic = 10 #总票数
def getTickets(num):
    print('正在准备购买%d张票!:'%num)
    sleep(2)
    print('成功购买了%d张票!'%num)
    global totalTic
    totalTic -= 3

if __name__ == '__main__':
    #创建一个进程,并且让进程表示了一组即将被执行的任务
    p = Process(target=getTickets,args=(3,))
    
    p.start()#执行子进程
    p.join() #主进程等待子进程结束

    print('剩余车票:',totalTic)

守护进程daemon方法:

作用于子进程 ,守护进程会在主进程代码执行结束后就终止,守护进程内无法再开启子进程,否则抛出异常

语法:子进程对象.daemon = True

注意:主进程代码运行结束,守护进程随即终止

from multiprocessing import Process
import time

class MyThread(Process):
    def __init__(self,name):
        super().__init__()
        self.name = name
    #重写父类的方法
    def run(self):
        print('%s子进程准备执行'%self.name)
        time.sleep(1)
        print('%s进程执行结束'%self.name)
if __name__ == '__main__':
    print('我是主进程')
    #创建一个自定义的进程对象
    t = MyThread('zhangsan')
    t.daemon = True  # 守护进程
    #启动当前的进程对象(调用run)
    t.start()
    print('主进程执行结束')

那怎么样开启多个进程呢?

需求是:所有的子进程异步执行,然后所有的子进程全部执行完之后,在结束主进程,怎么搞?

from multiprocessing import Process
from time import sleep

def getTickets(i):
    print('子进程%d即将执行'%i)
    sleep(2)
    print('我是子线程%d执行结束'%i)

if __name__ == '__main__':
    print('主进程即将开始执行')
    p_list = []
    for i in range(3): #多个子进程的产生
        p = Process(target=getTickets,args=(i,))
        p.start()
        p_list.append(p)
        
    for p in p_list:
        p.join()   #堵塞join方法
    print('主进程执行结束')

线程

线程:线程是操作系统能够进行运算调度的最小单位(车间里的工人),也叫执行路径,它被包含在进程之中,线程是进程中的实际运作单位。一个进程中可以并发多个线程,每条线程并行执行不同的任务。

同一个进程内的多个线程是共享该进程的资源的,不同进程内的线程资源肯定是隔离的

每一个进程中至少会包含有一个线程,该线程叫做"主线程"

python实现线程

Python提供了几个用于多线程编程的模块,包括thread、threading和Queue模块等。

thread模块提供了基本的线程和锁的支持,threading提供了更高级别、功能更强的线程管理的功能。Queue模块允许用户创建一个可以用于多个线程之间共享数据的队列数据结构。

线程的实现和功能与进程相同,区别于调用模块

Threading模块

线程的创建 

from threading import Thread
import time
def func(i):
    print('线程启动')
    time.sleep(1)
    print('线程接受')

if __name__ == '__main__':
    print('我是主线程')
    th1 = Thread(target=func,args=(123,)) #创建线程对象,并传参
    
    th1.start()

join方法同上(进程)

daemon方法

多线程实现

线程的GIL锁

在同一个进程中只有一个线程可以获取cpu的使用权限,那么其他的线程就必须等待该线程的cpu使用权消失后才能使用cpu,即使多个线程直接不会相互影响,在同一个进程下也只有一个线程使用cpu,这样的机制称为全局解释器锁(GIL)

每一个 Python 线程,在 CPython 解释器中执行时,都会先锁住自己的线程,阻止别的线程执行。

作用:避免了大量的加锁解锁的繁琐操作,使数据更加安全,解决多线程间的数据完整性和状态同步

同步锁

一个进程中的一个线程只能使用一个cpu。要保证一个线程对应的操作在一段时间内被完整的直接完毕所加入的锁就是同步锁。

作用:因为有可能当一个线程在使用cpu时,该线程下的程序可能会遇到io阻塞操作,那么cpu就会切到别的线程上去,这样就有可能会影响到该程序结果的完整性。

from multiprocessing import Process,Lock

总结:保护不同的数据就应该加不同的锁。 GIL 与Lock是两把锁,保护的数据不一样,前者是解释器级别的(当然保护的就是解释器级别的数据,比如垃圾回收的数据),后者是保护用户自己开发的应用程序的数据,很明显GIL不负责这件事,只能用户自定义加锁处理,即Lock

import os
# print(os.cpu_count()) #返回计算机的核数
from threading import Thread
from multiprocessing import Process,Lock
import time
def search():
    print('现在剩余票数:',total_tic)
def get():
    global total_tic
    time.sleep(0.3)
    if total_tic >= 1:
        time.sleep(0.3)
        print('正在购买车票......')
        time.sleep(0.3)
        total_tic -= 1
        time.sleep(0.3)
    else:
        print('抢票失败.......')

def task(lock):
    search()
    lock.acquire()
    get()
    lock.release()
if __name__ == '__main__':
    total_tic = 3
    lock = Lock()
    for i in range(5):
        t = Thread(target=task,args=(lock,))
        t.start()

线程池

线程预先被创建并放入线程池中,同时处理完当前任务之后并不销毁而是被安排处理下一个任务,因此能够避免多次创建线程,从而节省线程创建和销毁的开销,能带来更好的性能和系统稳定性。

import time
from multiprocessing.dummy import Pool #线程池
from time import sleep
urls = [
    'www.1.com',
    'www.2.com',
    'www.3.com',
    'www.4.com',
    'www.5.com'
]
def get_request(url):
    print('正在请求:',url)
    sleep(1)
    print('请求结束:',url)
    return url
#创建线程池对象
start = time.time()
pool = Pool(5)
#一组任务对应的函数必须只能有一个参数
#map返回值(每一组任务返回的结果)是一个列表.
result_list = pool.map(get_request,urls)
print(result_list)
print('总耗时:',time.time()-start)

协程asyncio

多任务的异步协成,特殊函数的返回值

特殊的函数如果一个函数的定义被async关键字修饰,就被视为是一个特殊的函数.

特殊之处:1.特殊函数调用后,函数内部的程序语句不会被立即执行.2.特殊函数调用后,会返回一个对象(协程对象)

import asyncio
#特殊的函数
async def func():
    print('i am func')
    return 'hello world'
#返回一个协程对象
c = func()
print(c)

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值