多线程多进程
多线程
线程是操作系统能够进行运算调度的最小单位;它被包含在进程之中,是进程中的实际运作单位。
多线程,是指从软件或者硬件上实现多个线程并发执行的技术。具有多线程能力的计算机因有硬件支持而能够在同一时间执行多于一个线程,进而提升整体处理性能。
简单来说:线程是程序中一个单一的顺序控制流程;而多线程就是在单个程序中同时运行多个线程来完成不同的工作。
多线程是为了同步完成多项任务,不是为了提高运行效率,而是为了提高资源使用效率来提高系统的效率。多线程是在同一时间需要完成多项任务的时候实现的。
多线程的优缺点
优点:
1)、多线程技术可以加快程序的运行速度,使程序的响应速度更快,因为用户界面可以在进行其它工作的同时一直处于活动状态
2)、可以把占据长时间的程序中的任务放到后台去处理,同时执行其他操作,提高效率
3)、当前没有进行处理的任务时可以将处理器时间让给其它任务
4)、可以让同一个程序的不同部分并发执行,释放一些珍贵的资源如内存占用等等
5)、可以随时停止任务
6)、可以分别设置各个任务的优先级以优化性能
缺点:
1)、因为多线程需要开辟内存,而且线程切换需要时间因此会很消耗系统内存。
2)、线程的终止会对程序产生影响
3)、由于多个线程之间存在共享数据,因此容易出现线程死锁的情况
4)、对线程进行管理要求额外的 CPU开销。线程的使用会给系统带来上下文切换的额外负担。
import time
from datetime import datetime
from threading import Thread, current_thread
# 程序默认都是单线程(这个默认线程又叫主线程,其他的线程都是子线程)
# Thread类的对象就是线程对象,程序需要多少个子线程,就创建多个Thread的对象
def download(name):
print(f'{name}开始下载:{datetime.now()}')
print('当前线程', current_thread())
time.sleep(2)
print(f'{name}下载结束:{datetime.now()}')
# 1.单线程下载三部电影
# download('大话西游')
# download('忍者神龟')
# download('黑寡妇')
# 2.多线程下载三部电影
"""
创建子线程:
Thread(target=函数, args=元组)
函数 - 需要子线程中调用的函数
元组 - 调用target对应的函数的时候传递实参列表
"""
# 1)创建线程对象
t1 = Thread(target=download, args=('大话西游',))
t2 = Thread(target=download, args=('忍者神龟',))
t3 = Thread(target=download, args=('黑寡妇',))
# 2)启动线程
# 线程对象.start()
t1.start()
t2.start()
t3.start()
多线程下载图片
import requests
from lxml import etree
from threading import Thread
def get_net_data(page):
if page == 1:
url = 'https://pic.netbian.com/index.html'
else:
url = f'https://pic.netbian.com/index_{page}.html'
response = requests.get(url)
response.encoding = 'gbk'
html = etree.HTML(response.text)
# 得到图片地址
img_urls = html.xpath('//div[@class="slist"]/ul[@class="clearfix"]/li/a/img/@src|//div[@class="slist"]/ul[@class="clearfix"]/li/a/span/img/@src')
img_urls = ['https://pic.netbian.com' + x for x in img_urls]
# 创建线程下载图片
t = Thread(target=dowenload_page_image, args=(img_urls,))
t.start()
def get_all_data():
# 爬取10页数据
for page in range(1, 11):
# 添加线程
t = Thread(target=get_net_data, args=(page,))
t.start()
def dowenload_page_image(urls):
for url in urls:
dowenload_image(url)
def dowenload_image(url):
# 下载图片到文件夹
response = requests.get(url)
f = open(f'files/{url.split("/")[-1]}', 'wb')
f.write(response.content)
get_all_data()
线程类得子类
from threading import Thread, current_thread
import time
from datetime import datetime
class DownloadThread(Thread):
def __init__(self, name):
super().__init__()
self.name = name
# 在子线程中添加任务
# run()不可以传参,传了也收不到,因为run()是其他的函数调用的。传参只能通过对象变量来传入。
def run(self):
print(f'{self.name}开始下载:{datetime.now()}')
print('当前线程', current_thread())
time.sleep(2)
print(f'{self.name}下载结束:{datetime.now()}')
# 传参数只能通过对象变量来传入
t1 = DownloadThread('奥特曼')
t2 = DownloadThread('葫芦娃')
t3 = DownloadThread('金刚')
# 通过start方法调用run方法,run方法会在响应的子线程中执行
t1.start()
t2.start()
t3.start()
join操作
from threading import Thread, current_thread
import time
from datetime import datetime
from random import randint
# join的用法;
# 线程对象.join() - 等当前线程的任务结束后才执行后面的代码
def download(name):
print(f'{name}开始下载:{datetime.now()}')
print('当前线程', current_thread())
time.sleep(2)
print(f'{name}下载结束:{datetime.now()}')
t1 = Thread(target=download, args=('大话西游',))
t2 = Thread(target=download, args=('忍者神龟',))
t3 = Thread(target=download, args=('黑寡妇',))
# 2)启动线程
# 线程对象.start()
t1.start()
t2.start()
t3.start()
t1.join()
print('-----------------电影全部下载结束----------------')
多进程
进程(Process)是具有一定独立功能的程序关于某个数据集合上的一次运行活动,是系统进行资源分配和调度的一个独立单位。程序在计算机中的一次执行过程。
多进程
1).意义:充分的利用计算机资源提高程序的运行效率
2).定义:通过应用程序利用计算机的多核资源达到同时执行多个任务的目的,以此来提升程序的执行效率
3).并行:多个计算机核心在同时处理多个任务,这多个任务间是并行关系
4).并发:同时处理多个任务,内核在任务间不断的切换,达到好像都在处理运行的效果
优点:多个进程都有自己的独立地址空间,一个进程结束不会影响其他进程。
缺点:多进程进行任务切换时,由于每个进程地址空间独立,会不停地刷新cahce高速缓存器和tlb列表页,所以比较浪费CPU资源
from multiprocessing import Process, current_process
from datetime import datetime
import time
from random import randint
def download(name):
print(f'{name}开始下载:{datetime.now()}')
print('当前线程', current_process())
time.sleep(randint(3, 7))
print(f'{name}下载结束:{datetime.now()}')
if __name__ == '__main__':
# 1.创建进程现象
p1 = Process(target=download, args=('肖生克的救赎',))
p2 = Process(target=download, args=('触不可及',))
p3 = Process(target=download, args=('雪国列车',))
# 2.启动进程
p1.start()
p2.start()
p3.start()
p1.join()
p2.join()
p3.join()
print('电影下载完成!')
线程间的通信
from threading import Thread
import time
# 在同一个进程中的多个线程间的数据可以直接相互使用
a = 100
list1 = []
def func1():
global a
a = 300
list1.append(200)
t = Thread(target=func1)
t.start()
t.join()
print(a, list1)
# 2.线程间通信的数据安全
# 加锁:一个数据一个锁(数据和锁需要对应)
from threading import Lock, RLock
"""
Lock对象:锁对象.acquire() - 加锁
锁对象.release() - 释放锁
RLock对象:
with 锁对象:
操作数据代码
"""
print('-----------------------')
balance = 10000
# 1)创建锁对象,保证一个数据一把锁
lock = Lock()
def save_money(num: int):
'''存钱'''
print('开始存钱')
# 2)在使用需要锁的数据之前加锁
lock.acquire()
global balance
b1 = balance
time.sleep(1)
balance = b1 + num
# 3)在数据使用完以后释放锁
lock.release()
print('余额', balance)
def draw_money(num: int):
print('开始取钱')
lock.acquire()
global balance
b1 = balance
if b1 >= num:
time.sleep(1)
balance = b1 - num
else:
print('余额不足!')
lock.release()
t1 = Thread(target=save_money, args=(2000, ))
t2 = Thread(target=draw_money, args=(3000, ))
t1.start()
t2.start()
t1.join()
t2.join()
print('余额', balance)
多线程的数据返回问题
from threading import Thread, current_thread
from multiprocessing import Process
# 结论:在子线程中调用的函数如果有返回,这个返回值是无法在任何地方获取
data = []
def download(name):
"""收集数据"""
print('-----')
# return f'{name}数据'
data.append(f'{name}数据')
def use_data():
for x in data:
print(f'使用{current_thread()}:', x)
if __name__ == '__main__':
t1 = Thread(target=download, args=('肖生克的救赎',))
t2 = Thread(target=download, args=('触不可及',))
t1.start()
t2.start()
t1.join()
t2.join()
print(data)
# t3 = Thread(target=use_data)
# t3.start()