1113day11:进程、线程、协程、资源、多线程、多进程、资源共享等基础知识

一、如何在Python中实现多线程?

Python中的多线程可以通过导入threading模块来实现。

二、进程

程序,文件在硬盘上、不能直接运行,先读到内存里面,先给他分配一个进程IP,分为父进程和子进程

开多少个进程:和CPU核数有关,12核就开12个进程

2.1、加进程和不接进程的时间对比

I.不加进程

import re  #正则
import requests  #获取请求
import json
import html
import os  #进行文件操作
import time


begin_time = time.time()
source = requests.get('http://2t6y.mydown.com/yuanqidesktop/tj.html?softid=585&tid1=5&tid2=1001&tod1=94&bd_vid=11323870191789086384').text  #获取源码,主要是获取文字,用text以文本的形式获取数据

demo = re.compile('dynamicWallpapers:(\[.*?\])',re.S)  #转义[]
lists = demo.findall(source)[0]  #匹配

demo = re.compile('\{name:"(.*?)",image:"(.*?)",video:"(.*?)"\}',re.S)  #name:图片名字,image:图片,video:动态图片

lists = demo.findall(source)

# 创建文件夹
if not os.path.isdir('壁纸'):  #先判断路径下有没有'壁纸'这个文件
    os.mkdir('壁纸')  #创建'壁纸'文件夹    makedirs()可以创建多个文件夹

for a,b,c in lists:
    img = b.encode('utf-8').decode("unicode_escape")   #python3把\u002F这种类型的字符串进行对应的替换
    video = c.encode('utf-8').decode("unicode_escape")  #python3把\u002F这种类型的字符串进行对应的替换

    img_source = requests.get(img).content  #请求,content意思就是获取二进制数据
    video_source = requests.get(video).content  #请求,获取图片本体,图片,动态图总的来说是以二进制数据保存的

    if not os.path.isdir('壁纸/{}'.format(a)):  #先判断路径下有没有'a'这个文件
        os.mkdir('壁纸/{}'.format(a))  #判断当前路径下有没有壁纸这个文件夹

    #保存图片
    op = open('壁纸/{}/{}.jpg'.format(a,a),'wb')  #没有的话打开壁纸文件夹开始以a创建新的
    op.write(img_source)  #写入
    op.close()  #关闭
    op = open('壁纸/{}/{}.mp4'.format(a, a), 'wb') #权限'wb',覆盖写,b意思是操作二进制数据,直接存文字,字符串啥的,直接用w就可以了
    op.write(video_source)
    op.close()

stop_time = time.time()
print(stop_time-begin_time)

结果:2.415644884109497

II.加进程

import re  #正则
import requests  #获取请求
import json
import html
import os  #进行文件操作
import time
from multiprocessing import Pool

def save_img(img_name,img_url,video_url):
    img_source = requests.get(img_url).content  # 请求,content意思就是获取二进制数据
    video_source = requests.get(video_url).content  # 请求,获取图片本体,图片,动态图总的来说是以二进制数据保存的

    if not os.path.isdir('壁纸/{}'.format(img_name)):  # 先判断路径下有没有'a'这个文件
        os.mkdir('壁纸/{}'.format(img_name))  # 判断当前路径下有没有壁纸这个文件夹

    # 保存图片
    op = open('壁纸/{}/{}.jpg'.format(img_name, img_name), 'wb')  # 没有的话打开壁纸文件夹开始以a创建新的
    op.write(img_source)  # 写入
    op.close()  # 关闭
    op = open('壁纸/{}/{}.mp4'.format(img_name, img_name), 'wb')  # 权限'wb',覆盖写,b意思是操作二进制数据,直接存文字,字符串啥的,直接用w就可以了
    op.write(video_source)
    op.close()

source = requests.get('http://2t6y.mydown.com/yuanqidesktop/tj.html?softid=585&tid1=5&tid2=1001&tod1=94&bd_vid=11323870191789086384').text  #获取源码,主要是获取文字,用text以文本的形式获取数据

demo = re.compile('dynamicWallpapers:(\[.*?\])',re.S)  #转义[]
lists = demo.findall(source)[0]  #匹配

demo = re.compile('\{name:"(.*?)",image:"(.*?)",video:"(.*?)"\}',re.S)  #name:图片名字,image:图片,video:动态图片

lists = demo.findall(source)

# 创建文件夹
if not os.path.isdir('壁纸'):  #先判断路径下有没有'壁纸'这个文件
    os.mkdir('壁纸')  #创建'壁纸'文件夹    makedirs()可以创建多个文件夹

if __name__=='__main__':    #调试
    begin_time = time.time()
    p = Pool(4)
    for a,b,c in lists:
        img = b.encode('utf-8').decode("unicode_escape")   #python3把\u002F这种类型的字符串进行对应的替换
        video = c.encode('utf-8').decode("unicode_escape")  #python3把\u002F这种类型的字符串进行对应的替换
        p.apply_async(save_img,a,img,video)  #发几个请求save_img()函数
    p.close()
    p.join()
    stop_time = time.time()
    print(stop_time-begin_time)

结果:1.2040400505065918s

三、线程

竞争机制,切换也消耗资源
开多少个线程合适:用工具测试,或者尝试找一个差不多的,决定可以开多少个线程 

3.1、面试题:python的多线程是不是真正的多线程,或者说异步?

答:是,但是,因为它上面有GIL全局解释器锁,导致它单位时间以内只能有一个线程在工作,极大的限制了他的效率。一般情况下我们不用多线程,开多进程就可以。

四、协程

机制和线程有点像,但是跟线程不一样,协程实际上是一个挂起的操作。

比如说我的程序做一个IO操作,跑一个爬虫,多长时间返回给你,对CPU来说,这4~5秒已经是一个很长的时间了,他就需要等,那这个时候用协程,会怎么样,用协程,他会把这个程序,假设让主进程等,行,我知道了,那就这个先等着。
我继续往下干其他的事情,我主程序将会继续往下走,然后直到网站的数据请求回来了,我就会停止相关的操作,但是在等的过程中,我的主程序是往下走的。
相当于暂时把这件事挂起,直到这件事完成,然后我在进行和这件事相关的操作,当这件事没完成的时候,我这个爬虫的程序是继续往下走的。

这么来理解,线程是一个竞争机制,需要靠抢,那么协程就是一个安排好的机制。加入有3件事情,什么时候干什么事情已经安排好了。

多进程可以和线程一起用,也可以和协程一起用。

但是线程和协程是不能一起用的,他是两套方法,当然协程这套方案更好。所以一般用多进程+协程来用,一般不用多进程+线程,基本没人用,效率低。

五、资源共享

进程的资源不共享,线程的资源共享。

5.1、进程的基础资源不共享,但是必须要用指定的方法通信,代码如下:

5.1.1、用多进程里面的Queue:这样from multiprocessing import Pool,Process,Queue
import os
from multiprocessing import Pool,Process,Queue
import time
import random

def write(q):
    print('进程号1:',os.getpid())
    for i in ['a','b','c']:
        q.put(i)  #往队列里面放入abc
        time.sleep(random.random())  #模拟延迟

def read(q):
    print('进程号2:',os.getpid())
    while True:   #死循环保持监听
        value = q.get(True)
        print(value)

if __name__ == '__main__':
    q = Queue()  #实例化队列,可以理解为一个容器,用来传递数据,往里面存东西或者取东西
    p1= Process(target=write,args=(q,))
    p2 = Process(target=read, args=(q,))
    p1.start()
    p2.start()  #启动
    p1.join()   #等待结束
    p2.terminate()  #人工终止这个进程

结果:
进程号14636
进程号25232
a
b
c

代码结论:写的里面没有打印,我在读的里面打印abc,那就是说read这里面确实从队列里面读出东西来了,
是我从write里面传的abc,由以上代码可知,进程之间实现了通信。
5.1.2、用普通的queue:这样from queue import Queue
from queue import Queue

q = Queue()
q.put('李白')
value = q.get(True)
print(value)

结果:李白

注意:用普通的queue,这样from queue import Queue,是不能实现进程之间通信的,
你必须用多进程里面的Queue:这样from multiprocessing import Pool,Process,Queue

六、python的GIL

GIL是python的==全局解释器锁==,同一进程中假如有多个线程运行,一个线程在运行python程序的时候会霸占python解释器(加了一把锁即GIL),是该进程内的其他线程无法运行,等该线程运行完后其他线程才能运行。

如果线程运行过程中遇到耗时操作,则解释器锁解开,使其他线程运行。所以在多线程中,线程的运行仍是有先后顺序的,并不是同时进行的。

多进程中因为每个进程都能被系统分配资源,相当于每个进程都有了一个python解释器,所以多进程可以实现多个进程的同时运行,缺点是进程系统资源开销大。

七、简述多线程、多进程

使用多进程和多线程编程,可以同时处理多个任务,充分使用CPU性能,节约时间等。

很多编程任务到最后都发展到需要同时处理多任务,就比如APP自动化测试,如果一个脚本一次只能在一台设备上测试APP,那就显得有些“低效”了,所以学习多线程和多进程是很有必要的。

7.1、进程:是操作系统结果的基础

1、操作系统进行资源分配和调度的基本单位,多个进程之间相互独立。

2、稳定性好,如果一个进程崩溃,不影响其他进程,但是进程消耗资源大,开启的进程数量有限制

7.2、线程:有时被称为轻量级进程,是程序执行流的最小单元。线程是进程中的一个实体

1、cpu进行资源分配和调度的基本单位,线程是进程的一部分,是比进程更小的能独立运行的基本单位,一个进程下的多个线程可以共享该进程的所有资源。

2、如果IO操作密集,则可以多线程运行效率高,缺点是一个线程崩溃,都会造成进程的崩溃。

7.3、应用:

1、IO密集的用多线程,在用户输入,sleep时候,可以切换到其他线程执行,减少等待时间。

2、CPU密集的多进程,因为假如IO操作少,用多线程的话,因为线程共享一个全局解释器锁,
当前运行的线程会霸占GIL,其他线程没有GIL,就不能充分利用多核CPU的优势。

7.4、区别:

1、一个进程可以包含多个线程,但是线程不能包含多个进程。

2、进程和线程的区别在于,子进程和父进程有不同的代码和数据空间,而多个线程则共享数据空间,每个线程有自己的执行堆栈和程序计数器为其执行上下文。

7.5、以下的代码是作为一个示范,显示如何创建多线程以及多进程。

第一种:多线程实践

import threading
from time import sleep,ctime

#定义好两个任务
def talk(content,count):
    for i in range(count):
        print(f'start to talk{content,ctime()}')
        sleep(3)

def write(content,count):
    for i in range(count):
        print(f'start to write{content, ctime()}')
        sleep(2)

threads = []

#创建好2个线程,并装载到线程列表里
t1 = threading.Thread(target=talk,args=('hello world',2))
threads.append(t1)

t2 = threading.Thread(target=write,args=('oh my god',2))
threads.append(t2)

if __name__=='__main__':
    for thread in threads:
        thread.start()

    for thread in threads:
        thread.join()  #join方法作用是主线程等待调用join方法的子线程终止后再往下执行

结果:
start to talk('hello world', 'Thu Nov 17 17:11:55 2022')
start to write('oh my god', 'Thu Nov 17 17:11:55 2022')
start to write('oh my god', 'Thu Nov 17 17:11:57 2022')
start to talk('hello world', 'Thu Nov 17 17:11:58 2022

第二种:多进程实践:基本原理和多线程相似,知识导入的模块不一样

import multiprocessing
from time import sleep,ctime

#定义好两个任务
def talk(content,count):
    for i in range(count):
        print(f'start to talk{content,ctime()}')
        sleep(3)

def write(content,count):
    for i in range(count):
        print(f'start to write{content, ctime()}')
        sleep(2)

processes = []

#创建好2个线程,并装载到进程列表里
p1 = multiprocessing.Process(target=talk,args=('hello world',3))
processes.append(p1)

p2 = multiprocessing.Process(target=write,args=('oh my god',3))
processes.append(p2)

if __name__=='__main__':
    for process in processes:
        process.start()

    for process in processes:
        process.join()   #join方法作用是主进程等待调用join方法的子进程终止后再往下执行

结果:
start to talk('hello world', 'Thu Nov 17 17:10:46 2022')
start to write('oh my god', 'Thu Nov 17 17:10:46 2022')
start to write('oh my god', 'Thu Nov 17 17:10:48 2022')
start to talk('hello world', 'Thu Nov 17 17:10:49 2022')
start to write('oh my god', 'Thu Nov 17 17:10:50 2022')
start to talk('hello world', 'Thu Nov 17 17:10:52 2022')
  • 1
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 0
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

ゆきな

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值