当前位置: 首页 > news >正文

爬虫 — 多线程

目录

  • 一、多任务概念
  • 二、实现多任务方式
    • 1、多进程 (Multiprocessing)
    • 2、多线程(Multithreading)
    • 3、协程(Coroutine)
  • 三、多线程执行顺序
  • 四、多线程的方法
    • 1、join()
    • 2、setDaemon()
    • 3、threading.enumerate()
  • 五、继承 Thread 类创建线程
  • 六、线程间的通信(多线程共享全局变量)
  • 七、互斥锁和死锁
    • 1、互斥锁
    • 2、死锁
  • 八、生产者与消费者模式
    • 1、Queue 线程队列
    • 2、生产者和消费者
  • 九、案例
    • 1、单线程实现
    • 2、多线程实现
  • 十、作业

一、多任务概念

多任务(Multitasking)是指在同一时间内执行多个任务或进程的能力。它可以以不同的方式实现,包括多进程、多线程和协程等。

二、实现多任务方式

1、多进程 (Multiprocessing)

多进程是指同时运行多个独立的进程,每个进程有自己的地址空间和系统资源。多进程可以在多个处理器核心上并行执行任务,每个进程拥有独立的执行环境,相互之间不受影响。

进程(Process)

进程是计算机中运行的程序的实例。每个进程都拥有独立的内存空间和系统资源。一个进程可以包含多个线程。

2、多线程(Multithreading)

多线程是指在一个进程中同时执行多个线程的编程模型。线程是进程内的执行单元,每个线程独立执行特定的任务,但共享同一进程的内存空间。多线程编程可以提高程序的并发性和响应性。

线程(Thread)

线程是操作系统能够进行调度的最小单位。它包含了执行代码所需的上下文信息(如程序计数器、栈、寄存器等),可以独立运行和调度。多个线程可以在同一时间内执行不同的任务。

主线程(Main Thread)

主线程是程序启动时默认创建的第一个线程。主线程负责执行程序的入口点,并可以创建其它线程。

3、协程(Coroutine)

协程是一种轻量级的并发编程技术,它可以在单线程中实现多个独立的执行流程,从而提供高效的并发和协作。与线程相比,协程的切换开销更小,且没有多线程中的锁和同步机制的复杂性。协程拥有自己的寄存器上下文和栈。协程调度切换时,将寄存器上下文和栈保存到其它地方,在切回来的时候,恢复先前保存的寄存器上下文和栈。

寄存器上下文(Register Context)

是指存储在处理器寄存器中的一组值,用于保存正在执行的程序的状态信息。寄存器上下文包含了程序计数器、栈指针、通用寄存器等寄存器的值。

并发(Concurrency)

并发是指多个任务同时进行,但不一定同时完成。在多线程编程中,线程可以并发执行,通过时间片轮转等方式实现看似同时执行的效果。(资源够用,比如三个线程,四核的 CPU。)

并行(Parallelism)

并行是指多个任务同时进行且同时完成。在多核处理器上,多个线程可以被映射到不同的核上并行执行。(比如单核 CPU 资源,同时只能运行一个任务,A 运行一段后,让给 B,B 用完继续给 A,交替使用,提高效率。)

三、多线程执行顺序

# 时间模块
import timedef task():print("hello python")time.sleep(1)print("hello world")
for i in range(5):task()# hello python
# hello world
# hello python
# hello world
# hello python
# hello world
# hello python
# hello world
# hello python
# hello world
# 时间模块
import time
# 多线程模块
import threading# 子线程
def task():print("hello python")time.sleep(1)print("hello world")# 主线程
if __name__ == '__main__':for i in range(5): # 循环5次,创建了5个线程对象# 创建线程对象,target 是执行任务t = threading.Thread(target=task)# 多线程为开始工作状态t.start()# hello python
# hello python
# hello python
# hello python
# hello python
# hello world
# hello world
# hello world
# hello world
# hello world

四、多线程的方法

1、join()

等待子线程结束之后,主线程继续执行。

谨慎使用,假设子线程当中有一个死循环,子线程不结束,主线程能不能结束。

# 时间模块
import time
# 多线程模块
import threading# 子线程
def task():print("hello python")time.sleep(1)print("hello world")# 主线程
if __name__ == '__main__':for i in range(5): # 循环5次,创建了5个线程对象# 创建线程对象,target 是执行任务t = threading.Thread(target=task)# 多线程为开始工作状态t.start()# 子线程结束了才会执行后面的代码t.join()# hello python
# hello world
# hello python
# hello world
# hello python
# hello world
# hello python
# hello world
# hello python
# hello world

2、setDaemon()

守护线程,不会等待子线程结束。

# 时间模块
import time
# 多线程模块
import threading# 子线程
def task():print("hello python")time.sleep(1)print("hello world")# 主线程
if __name__ == '__main__':for i in range(5): # 循环5次,创建了5个线程对象# 创建线程对象,target 是执行任务t = threading.Thread(target=task)# 守护线程:主线程结束程序就立马结束了,不会影响到主线程的运行t.setDaemon(True)# 多线程为开始工作状态t.start()# hello python
# hello python
# hello python
# hello python
# hello python

3、threading.enumerate()

查看当前线程的数量。

# 时间模块
import time
# 多线程模块
import threading# 子线程
def sing():for i in range(3):print(f'正在唱歌。。。{i}')time.sleep(0.5)# 子线程
def dance():for i in range(3):print(f'正在跳舞。。。{i}')time.sleep(0.5)# 主线程
if __name__ == '__main__':# 创建线程对象t1 = threading.Thread(target=sing)t2 = threading.Thread(target=dance)# 开启线程t1.start() # start 开启时,子线程才算创建t2.start()# 查看线程数量# 2子1主,共3个线程print(threading.enumerate()) # [<_MainThread(MainThread, started 8584)>, <Thread(Thread-1, started 5504)>, <Thread(Thread-2, started 18404)>]

五、继承 Thread 类创建线程

# 时间模块
import time
# 多线程模块
import threading# 创建的是类,继承线程类,就具备线程的特性
class MyThread1(threading.Thread):# 重写父类的 run 方法,start 触发 run 方法def run(self):for i in range(5):print(f'MyThread1---{i}')time.sleep(1)class MyThread2(threading.Thread):def run(self):for i in range(5):print(f'MyThread2---{i}')time.sleep(1)if __name__ == '__main__':# 创建对象mt = MyThread1()mt1 = MyThread2()# 开启线程mt.start()mt1.start()

六、线程间的通信(多线程共享全局变量)

在一个函数中,对全局变量进行修改的时候,如果修改了指向,必须使用 global,仅仅是修改了指向空间中的数据时,不用必须使用 global。

线程是共享全局变量的。

import threading # 导入线程模块# 定义全局变量 num,初始值为0
num = 0# 定义函数 task
def task():# 在函数内部使用全局变量 numglobal num# 循环数据for i in range(10000000): # 1千万num += 1# 打印当前 num 的值print("task--num=%d" % num)# 定义函数 task1
def task1():# 在函数内部使用全局变量 numglobal num# 循环数据for i in range(10000000):  # 1千万num += 1# 打印当前 num 的值print(f"task1 num={num}")# 主程序
if __name__ == '__main__':# 创建一个线程对象 t,执行函数 taskt = threading.Thread(target=task)# 创建一个线程对象 t1,执行函数 task1t1 = threading.Thread(target=task1)# 启动线程 tt.start()# 启动线程 t1t1.start()# 打印当前 num 的值(在两个子线程运行之前打印)print(f"main--num={num}")

七、互斥锁和死锁

1、互斥锁

当多个线程几乎同时修改某一个共享数据的时候,需要进行同步控制。

某个线程要更改共享数据时,先将其锁定,此时,资源的状态为“锁定”,其它线程不能改变,直到该线程释放资源,将资源的状态变成“非锁定”,其它的线程才能再次锁定该资源。

互斥锁保证了每次只有一个线程进入写入操作,从而保证了多线程情况下数据的正确性。

# 创建锁
mutex = threading.Lock()
# 锁定
mutex.acquire()
# 释放
mutex.release()
import threading # 导入线程模块
import time # 导入时间模块# 定义全局变量 num,初始值为0
num = 0# 定义函数 task
def task(nums):# 在函数内部使用全局变量 numglobal num# 获取互斥锁,确保线程安全mutex.acquire()# 循环数据for i in range(nums):num += 1# 释放互斥锁mutex.release()# 打印当前 num 的值print("task--num=%d" % num)# 定义函数 task1
def task1(nums):# 在函数内部使用全局变量 numglobal num# 获取互斥锁,确保线程安全mutex.acquire()# 循环数据for i in range(nums):num += 1# 释放互斥锁mutex.release()# 打印当前 num 的值print(f"task1 num={num}")# 主程序
if __name__ == '__main__':# 创建互斥锁对象mutex = threading.Lock()# 定义 nums 的值nums = 10000# 创建一个线程对象 t,执行函数 taskt = threading.Thread(target=task, args=(nums,), ) # 传参,数据类型必须是元组# 创建一个线程对象 t1,执行函数 task1t1 = threading.Thread(target=task1, args=(nums, ))# 启动线程 tt.start()# 启动线程 t1t1.start()# 主线程等待2秒,确保子线程执行完毕time.sleep(2)# 打印当前 num 的值print(f"main--num={num}")

2、死锁

在线程间共享多个资源的时候,如果两个线程分别占有一部分资源并且同时等待对方的资源,就会造成死锁。

八、生产者与消费者模式

1、Queue 线程队列

Queue(队列)是一个线程安全的数据结构,常用于在多线程编程中实现线程间的通信和数据共享。

Python 中的 queue 模块中提供了同步的、线程安全的队列类,包括 FIFO(先进先出)队列 Queue,LIFO(后入先出)队列 LifoQueue。

这些队列都实现了锁原语(可以理解为原子操作,即要么不做,要么都做完),能够在多线程中直接使用,使用队列可以实现线程间的同步。

队列方法

  • 初始化 Queue(maxsize):创建一个先进先出的队列。
  • empty():判断队列是否为空。
  • full():判断队列是否满了。
  • get():从队列中取最后一个数据。
  • put():将一个数据放到队列中。
from queue import Queue # 导入队列模块中的 Queue 类,用于使用队列数据结构# 实例化对象,队列充当的是容器
# 初始化 Queue(maxsize)
q = Queue(5) # maxsize 为5,就只能存5组数据,可以存放任何类型的数据
q.put(1) # 往队列当中添加值
q.put({"key":"value"})
q.put([2, 3, 4])
q.put(3.5)
q.put(True)
# q.put(4) # 超出队列大小,程序会出现阻塞
print('----', q.qsize()) # 查看队列的大小# 取值
print(q.get())
print(q.get())
print('----', q.qsize()) # 查看队列的大小,取出后的值不在队列中了# 判断队列是否满了
print(q.full()) # False 3
print(q.empty()) # 判断队列是否为空,如果是空返回的是 True

2、生产者和消费者

生产者和消费者模式是多线程开发中常见的一种模式。通过这种模式,可以让代码达到高内聚低耦合的目标,线程管理更加方便,程序分工更加明确。

生产者的线程专门用来生产一些数据,然后存放到容器(中间变量)中,消费者再从这个中间的容器中取出数据进行消费。

在这里插入图片描述

from queue import Queue # 导入 Queue 模块,用于使用队列数据结构
import threading # 导入 threading 模块,用于多线程编程
import time # 导入 time 模块,用于时间相关操作# 定义函数,用于向队列中存值
def set_value(q):num = 0while True:# 将值放入队列q.put(num)# 值自增num += 1# 线程休眠0.5秒time.sleep(0.5)# 定义函数,用于从队列中获取值并打印
def get_value(q):while True:# 从队列中获取值并打印print(q.get())# 主程序
if __name__ == '__main__':# 创建一个大小为4的队列对象q = Queue(4)# 创建一个子线程,调用 set_value 函数,用于存值t1 = threading.Thread(target=set_value, args=(q,))# 创建一个子线程,调用 get_value 函数,用于取值t2 = threading.Thread(target=get_value, args=(q,))# 启动线程 t1t1.start()# 启动线程 t2t2.start()

九、案例

目标网站:https://qq.yh31.com/zjbq/List_48.html

需求:爬取表情包图片,并且将图片保存到文件夹中

1、单线程实现

页面分析

1、数据有多页,先获取第一页数据

2、确定 url,判断是静态加载还是动态加载

静态加载 url:https://qq.yh31.com/zjbq/List_48.html

3、解析数据

先获取到所有的 img 标签

循环遍历获取每一组的数据

4、获取翻页数据,观察 url 变化的规律

第一页:https://qq.yh31.com/zjbq/List_48.html

第二页:https://qq.yh31.com/zjbq/List_47.html

第三页:https://qq.yh31.com/zjbq/List_46.html

代码实现

import requests # 导入 requests 模块,用于发送网络请求
from lxml import etree # 导入 lxml 库中的 etree 模块,用于解析 HTML
import re # 导入 re 模块,用于正则表达式匹配# 定义函数,用于下载图片
def download_img():# 请求头head = {'User-Agent': 'Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/113.0.0.0 Safari/537.36'}# 获取5页数据for i in range(48, 43, -1):# 目标 urlurl = f'https://qq.yh31.com/zjbq/List_{i}.html'# 发送 get 请求,获取响应对象res = requests.get(url, headers=head)# 设置响应编码为 utf-8res.encoding = 'utf-8'# 打印响应内容# print(res.text)# 解析响应内容html = etree.HTML(res.text)# 获取所有的 img 标签images = html.xpath('//div[@class="zj_tp"]/a/img')# 遍历循环每一个 img 标签for img in images:# 获取图片 urlimg_url = img.xpath('@src')[0]# 获取图片标题img_title = img.xpath('@alt')[0]# 使用正则表达式替换标题中的特殊字符title = re.sub(r'[<>:?.()/\\]', '', img_title)# 打印图片 url 和标题# print(img_url, img_title)# 发送 get 请求,获取图片响应res = requests.get(img_url, headers=head)# 打开文件,将图片内容写入到文件中with open(f'pictures/{title}.jpg', 'wb') as f:f.write(res.content)print(f'{title}正在下载')# 调用下载图片的函数
download_img()

2、多线程实现

页面分析

用生产者与消费者下载表情包

一个是生产数据类,一个是下载数据类

队列只是一个容器

代码实现

import requests # 导入 requests 库,用于发送 HTTP 请求
from lxml import etree # 导入 lxml 库,用于解析 HTML
import re # 导入 re 库,用于正则表达式操作
import threading # 导入 threading 库,用于多线程编程
from queue import Queue # 导入 Queue 类,用于创建队列# 请求头
head = {'User-Agent': 'Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/113.0.0.0 Safari/537.36'
}# 生产者类,用于获取图片链接
class Producer(threading.Thread):# 初始化方法def __init__(self, page_queue, img_queue):# 必须要执行父类当中的 init 方法完成初始化super().__init__()# 设置页码队列属性self.page_queue = page_queue# 设置图片队列属性self.img_queue = img_queue# 重写 run 方法def run(self):# 循环取队列里面的数据,直到队列数据为空while True:# 如果页码队列为空if self.page_queue.empty():# 退出循环break# 从页码队列中获取 urlurl = self.page_queue.get()# 打印 url# print(url)# 调用 parse_html 方法解析页面self.parse_html(url)# 定义解析页面的方法def parse_html(self, url):# 发送 get 请求,获取响应对象res = requests.get(url, headers=head)# 设置响应编码为 utf-8res.encoding = 'utf-8'# 打印响应内容# print(res.text)# 解析响应内容html = etree.HTML(res.text)# 获取所有的 img 标签images = html.xpath('//div[@class="zj_tp"]/a/img')# 遍历循环每一个 img 标签for img in images:# 获取图片 urlimg_url = img.xpath('@src')[0]# 获取图片标题img_title = img.xpath('@alt')[0]# 使用正则表达式替换标题中的特殊字符title = re.sub(r'[<>:?.()/\\]', '', img_title)# 将图片 url 和标题作为元组放入图片队列中self.img_queue.put((img_url, title))# 打印图片队列的大小# print(self.img_queue.qsize())# 消费者类,用于下载图片
class consumer(threading.Thread):# 初始化方法def __init__(self, img_queue):# 必须要执行父类当中的 init 方法完成初始化super().__init__()# 设置图片队列属性self.img_queue = img_queue# 重写 run 方法def run(self):# 循环取队列里面的数据,直到队列数据为空while True:# 打印图片队列的大小print(self.img_queue.qsize())# # 如果图片队列为空# if self.img_queue.empty():#     # 退出循环#     break# 从图片队列中获取图片数据img_data = self.img_queue.get()# 将图片数据解包为 url 和标题url, title = img_data# 发送 get 请求,获取图片响应res = requests.get(url, headers=head)# 打开文件,将图片内容写入到文件中with open(f'pictures/{title}.jpg', 'wb') as f:f.write(res.content)print(f'{title}正在下载')# 主程序
if __name__ == '__main__':# 存放 url 的队列page_queue = Queue()# 创建图片队列img_queue = Queue()# 循环页码for i in range(48, 43, -1):# 创建 urlurl = f'https://qq.yh31.com/zjbq/List_{i}.html'# url 放入页码队列page_queue.put(url)# 创建空列表lst = []# 创建生产者for i in range(3):# 将存放的 url 队列传递给生产者t = Producer(page_queue, img_queue)# 开启线程t.start()# 添加线程到列表lst.append(t)# # join:等子线程结束了才会执行主线程的代码# # 加 join 是生产完了再下载,不加是边生产边下载# # 如消费者 run 方法里判断图片队列为空,就需要加 join# for i in lst:#     i.join()# 创建消费者for i in range(3):# 将图片队列传递给消费者t1 = consumer(img_queue)# 开启线程t1.start()

十、作业

目标网站:https://www.fabiaoqing.com/biaoqing/lists/page/1.html

需求:爬取表情包图片,并且将图片保存到文件夹中

import requests # 导入 requests 库,用于发送 HTTP 请求
from lxml import etree # 导入 lxml 库,用于解析 HTML
import re # 导入 re 库,用于正则表达式操作
import threading # 导入 threading 库,用于多线程编程
from queue import Queue # 导入 Queue 类,用于创建队列# 请求头
head = {'Referer':'https://www.fabiaoqing.com/','User-Agent': 'Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/113.0.0.0 Safari/537.36'
}# 生产者类,用于获取图片链接
class Producer(threading.Thread):# 初始化方法def __init__(self, page_queue, img_queue):# 必须要执行父类当中的 init 方法完成初始化super().__init__()# 设置页码队列属性self.page_queue = page_queue# 设置图片队列属性self.img_queue = img_queue# 重写 run 方法def run(self):# 循环取队列里面的数据,直到队列数据为空while True:# 如果页码队列为空if self.page_queue.empty():# 退出循环break# 从页码队列中获取 urlurl = self.page_queue.get()# 打印 url# print(url)# 调用 parse_html 方法解析页面self.parse_html(url)# 定义解析页面的方法def parse_html(self, url):# 发送 get 请求,获取响应对象res = requests.get(url, headers=head)# 设置响应编码为 utf-8res.encoding = 'utf-8'# 打印响应内容# print(res.text)# 解析响应内容html = etree.HTML(res.text)# 获取所有的 img 标签images = html.xpath('//div[@class="tagbqppdiv"]/a/img')# 遍历循环每一个 img 标签for img in images:# 获取图片 urlimg_url = img.xpath('@data-original')[0]# 获取图片标题img_title = img.xpath('@alt')[0]# 使用正则表达式替换标题中的特殊字符title = re.sub(r'[<>:?.()/\\]', '', img_title)# 将图片 url 和标题作为元组放入图片队列中self.img_queue.put((img_url, title))# 打印图片队列的大小# print(self.img_queue.qsize())# 消费者类,用于下载图片
class consumer(threading.Thread):# 初始化方法def __init__(self, img_queue):# 必须要执行父类当中的 init 方法完成初始化super().__init__()# 设置图片队列属性self.img_queue = img_queue# 重写 run 方法def run(self):# 循环取队列里面的数据,直到队列数据为空while True:# 打印图片队列的大小print(self.img_queue.qsize())# # 如果图片队列为空# if self.img_queue.empty():#     # 退出循环#     break# 从图片队列中获取图片数据img_data = self.img_queue.get()# 将图片数据解包为 url 和标题url, title = img_data# 发送 get 请求,获取图片响应res = requests.get(url, headers=head)# 打开文件,将图片内容写入到文件中with open(f'pictures/{title}.jpg', 'wb') as f:f.write(res.content)print(f'{title}正在下载')# 主程序
if __name__ == '__main__':# 存放 url 的队列page_queue = Queue()# 创建图片队列img_queue = Queue()# 循环页码for i in range(1, 5, 1):# 创建 urlurl = f'https://www.fabiaoqing.com/biaoqing/lists/page/{i}.html'# url 放入页码队列page_queue.put(url)# 创建空列表lst = []# 创建生产者for i in range(3):# 将存放的 url 队列传递给生产者t = Producer(page_queue, img_queue)# 开启线程t.start()# 添加线程到列表lst.append(t)# # join:等子线程结束了才会执行主线程的代码# # 加 join 是生产完了再下载,不加是边生产边下载# # 如消费者 run 方法里判断图片队列为空,就需要加 join# for i in lst:#     i.join()# 创建消费者for i in range(3):# 将图片队列传递给消费者t1 = consumer(img_queue)# 开启线程t1.start()

记录学习过程,欢迎讨论交流,尊重原创,转载请注明出处~

相关文章:

爬虫 — 多线程

目录 一、多任务概念二、实现多任务方式1、多进程 &#xff08;Multiprocessing&#xff09;2、多线程&#xff08;Multithreading&#xff09;3、协程&#xff08;Coroutine&#xff09; 三、多线程执行顺序四、多线程的方法1、join()2、setDaemon()3、threading.enumerate() …...

Cython 笔记 (Python/Jython)

目录 1. Cython 笔记 (Python)2. python 加速库 cython 简介2.1. Cython 是什么?2.2. 如何安装 Cython?2.3. 简单示例2.4. 性能比对2.5. 总结 3. PYTHON, CYTHON, JYTHON, IRONPYTHON 的区别 (注意: 此篇有误导&#xff0c;表述不一定正确&#xff0c;只提供一个方向)3.1. PY…...

[React] react-hooks如何使用

react-hooks思想和初衷&#xff0c;也是把组件&#xff0c;颗粒化&#xff0c;单元化&#xff0c;形成独立的渲染环境&#xff0c;减少渲染次数&#xff0c;优化性能。 文章目录 1.为什么要使用hooks2.如何使用hooks2.1 useState2.2 useEffect2.3 useLayoutEffect2.4 useRef2.5…...

多个pdf合并成一个文件,3个方法合并pdf

如何把多个pdf合并成一个文件&#xff1f;在我们日常的工作中&#xff0c;经常会遇到一些需要处理的文件&#xff0c;其中包括PDF文件。特别是当我们需要将多个PDF文件合并成一个PDF文件时&#xff0c;会面临一些困难。这样的情况下&#xff0c;我们的阅读能力会受到限制&#…...

代码随想录 动态规划Ⅸ

198. 打家劫舍 你是一个专业的小偷&#xff0c;计划偷窃沿街的房屋。每间房内都藏有一定的现金&#xff0c;影响你偷窃的唯一制约因素就是相邻的房屋装有相互连通的防盗系统&#xff0c;如果两间相邻的房屋在同一晚上被小偷闯入&#xff0c;系统会自动报警。 给定一个代表每个…...

【数据结构】散列表(哈希表)的学习知识总结

目录 1、散列表 2、散列函数 2.1 定义 2.2 散列函数的构造 2.2.1 除留余数法 2.2.2 直接定址法 2.2.3 数字分析法 2.2.4 平方取中法 3、冲突&#xff08;碰撞&#xff09; 4、处理冲突的方法 4.1 拉链法&#xff08;链接法&#xff09; 4.2 开放定址法 5、C语言…...

2023智慧云打印小程序源码多店铺开源版 +前端

智慧自助云打印系统/智慧云打印小程序源码 前端 这是一款全新的基于Thinkphp的最新自助打印系统&#xff0c;最新UI界面设计的云打印小程序源码...

利用亚马逊 云服务器 EC2 和S3免费套餐搭建私人网盘

网盘是一种在线存储服务&#xff0c;提供文件存储&#xff0c;访问&#xff0c;备份&#xff0c;贡献等功能&#xff0c;是我们日常中不可或缺的一种服务。很多互联网公司都为个人和企业提供免费的网盘服务。但这些免费服务都有一些限制&#xff0c;比如限制下载速度&#xff0…...

数据分析技能点-数据的种类

在日常生活中,数据无处不在。当你去超市购物时,你可能会注意到商品的价格、重量、口味等;当你在社交媒体上浏览时,你可能会注意到好友的点赞数、评论等。这些都是数据的一种形式,而了解这些数据的种类和特点有助于我们更好地理解和使用它们。 数据的基本分类 数据大致可…...

解读:ISO 14644-21:2023《洁净室及相关受控环境:悬浮粒子采样》发布指导粒子采样!

药品洁净实验室环境监测结果是否满足微生物检测需求&#xff0c;直接决定检测结果的有效性准确性&#xff0c;进行药品微生物检测&#xff0c;必须对实验环境进行日常和定期监测&#xff0c;其内容包括非生物活性的空气悬浮粒子数及有生物活性的微生物监测。 悬浮粒子监测是保证…...

Java --- MySQL8之索引优化与查询优化

目录 一、索引失效场景 1.1、全值匹配 1.2、最佳左前缀规则 1.3、主键插入顺序 1.4、计算、函数、类型转换(自动或手动)导致索引失效 1.5、类型转换导致索引失效 1.6、范围条件右边的列索引失效 1.7、不等于(! 或者<>)索引失效 1.8、is null可以使用索引&…...

澳大利亚新版《2023年消费品(36个月以下儿童玩具) 安全标准》发布 旨在降低危险小零件的伤害

2023年9月4日&#xff0c;澳大利亚政府发布了新的儿童玩具强制性安全标准《2023年消费品(36个月以下儿童玩具)安全标准》&#xff08;Consumer Goods (Toys for Children up to and including 36 Months of Age) Safety Standard 2023&#xff09;。该强制性标准旨在尽可能地降…...

表格内日期比较计算

需求&#xff1a;在表格中新增数据&#xff0c;计算开始日期中最早的和结束日期中最晚的&#xff0c;回显到下方。 <el-formref"formRef":model"ruleForm":rules"rules"style"margin-top: 20px;"label-position"top">…...

Linux内核启动流程-第二阶段start_kernel 函数

一. Linux内核启动 上一篇文章简单介绍了 Linux内核启动的第一阶段&#xff0c;即执行汇编流程。 本文简单了解一下&#xff0c;Linux内核启动的第二阶段&#xff1a;start_kernel函数&#xff0c;这是一个 C 函数。 本文续上一篇文章的学习&#xff0c;地址如下&#xff1a;…...

Disruptor:无锁队列设计的背后原理

简介 在高并发场景下&#xff0c;队列的速度和效率是关键。而Disruptor&#xff0c;一种高性能的并发队列&#xff0c;通过独特的设计&#xff0c;解决了传统队列在处理高并发时可能遇到的性能瓶颈。本文将深入分析Disruptor如何通过环形数组结构、元素位置定位以及无锁设计&a…...

网络编程-UDP协议(发送数据和接收数据)

需要了解TCP协议的&#xff0c;可以看往期文章 https://blog.csdn.net/weixin_43860634/article/details/133274701 TCP/IP参考模型 通过此图&#xff0c;可以了解UDP所在哪一层级中 代码案例 发送数据 package com.hidata.devops.paas.udp;import java.io.IOException; …...

AI绘画普及课【一】绘画入门

文章目录 一、AI 绘画入门1、Stable Diffusion VS. MidJourney2、Stable Diffusion 介绍3、Stable Diffusion 环境搭建4、文生图与图生图 一、AI 绘画入门 1、Stable Diffusion VS. MidJourney Midjourney 优点: 操作简单、出图绚丽多彩 缺点: 订阅付费充钱 内容有限制&a…...

Selenium和Requests搭配使用

Selenium和Requests搭配使用 前要1. CDP2. 通过requests控制浏览器2. 1 代码一2. 2 代码2 3. 通过selenium获取cookie, requests携带cookie请求 前要 之前有提过, 用selenium控制本地浏览器, 提高拟人化,但是效率比较低,今天说一种selenium和requests搭配使用的方法 注意: 一定…...

【JDK 8-函数式编程】4.4 Supplier

一、Supplier 接口 二、实战 Stage 1: 创建 Student 类 Stage 2: 创建方法 Stage 3: 调用方法 Stage 4: 执行结果 一、Supplier 接口 供给型 接口: 无入参&#xff0c;有返回值&#xff08;T : 出参类型&#xff09; 调用方法: T get(); 用途: 如 无参的工厂方法&#x…...

后端大厂面试-16道面试题

1 java集合类有哪些&#xff1f; List是有序的Collection&#xff0c;使用此接口能够精确的控制每个元素的插入位置&#xff0c;用户能根据索引访问List中元素。常用的实现List的类有LinkedList&#xff0c;ArrayList&#xff0c;Vector&#xff0c;Stack。 ArrayList是容量…...

【Python】 -- 趣味代码 - 小恐龙游戏

文章目录 文章目录 00 小恐龙游戏程序设计框架代码结构和功能游戏流程总结01 小恐龙游戏程序设计02 百度网盘地址00 小恐龙游戏程序设计框架 这段代码是一个基于 Pygame 的简易跑酷游戏的完整实现,玩家控制一个角色(龙)躲避障碍物(仙人掌和乌鸦)。以下是代码的详细介绍:…...

突破不可导策略的训练难题:零阶优化与强化学习的深度嵌合

强化学习&#xff08;Reinforcement Learning, RL&#xff09;是工业领域智能控制的重要方法。它的基本原理是将最优控制问题建模为马尔可夫决策过程&#xff0c;然后使用强化学习的Actor-Critic机制&#xff08;中文译作“知行互动”机制&#xff09;&#xff0c;逐步迭代求解…...

23-Oracle 23 ai 区块链表(Blockchain Table)

小伙伴有没有在金融强合规的领域中遇见&#xff0c;必须要保持数据不可变&#xff0c;管理员都无法修改和留痕的要求。比如医疗的电子病历中&#xff0c;影像检查检验结果不可篡改行的&#xff0c;药品追溯过程中数据只可插入无法删除的特性需求&#xff1b;登录日志、修改日志…...

使用van-uploader 的UI组件,结合vue2如何实现图片上传组件的封装

以下是基于 vant-ui&#xff08;适配 Vue2 版本 &#xff09;实现截图中照片上传预览、删除功能&#xff0c;并封装成可复用组件的完整代码&#xff0c;包含样式和逻辑实现&#xff0c;可直接在 Vue2 项目中使用&#xff1a; 1. 封装的图片上传组件 ImageUploader.vue <te…...

什么?连接服务器也能可视化显示界面?:基于X11 Forwarding + CentOS + MobaXterm实战指南

文章目录 什么是X11?环境准备实战步骤1️⃣ 服务器端配置(CentOS)2️⃣ 客户端配置(MobaXterm)3️⃣ 验证X11 Forwarding4️⃣ 运行自定义GUI程序(Python示例)5️⃣ 成功效果![在这里插入图片描述](https://i-blog.csdnimg.cn/direct/55aefaea8a9f477e86d065227851fe3d.pn…...

laravel8+vue3.0+element-plus搭建方法

创建 laravel8 项目 composer create-project --prefer-dist laravel/laravel laravel8 8.* 安装 laravel/ui composer require laravel/ui 修改 package.json 文件 "devDependencies": {"vue/compiler-sfc": "^3.0.7","axios": …...

基于Java Swing的电子通讯录设计与实现:附系统托盘功能代码详解

JAVASQL电子通讯录带系统托盘 一、系统概述 本电子通讯录系统采用Java Swing开发桌面应用&#xff0c;结合SQLite数据库实现联系人管理功能&#xff0c;并集成系统托盘功能提升用户体验。系统支持联系人的增删改查、分组管理、搜索过滤等功能&#xff0c;同时可以最小化到系统…...

A2A JS SDK 完整教程:快速入门指南

目录 什么是 A2A JS SDK?A2A JS 安装与设置A2A JS 核心概念创建你的第一个 A2A JS 代理A2A JS 服务端开发A2A JS 客户端使用A2A JS 高级特性A2A JS 最佳实践A2A JS 故障排除 什么是 A2A JS SDK? A2A JS SDK 是一个专为 JavaScript/TypeScript 开发者设计的强大库&#xff…...

排序算法总结(C++)

目录 一、稳定性二、排序算法选择、冒泡、插入排序归并排序随机快速排序堆排序基数排序计数排序 三、总结 一、稳定性 排序算法的稳定性是指&#xff1a;同样大小的样本 **&#xff08;同样大小的数据&#xff09;**在排序之后不会改变原始的相对次序。 稳定性对基础类型对象…...

AD学习(3)

1 PCB封装元素组成及简单的PCB封装创建 封装的组成部分&#xff1a; &#xff08;1&#xff09;PCB焊盘&#xff1a;表层的铜 &#xff0c;top层的铜 &#xff08;2&#xff09;管脚序号&#xff1a;用来关联原理图中的管脚的序号&#xff0c;原理图的序号需要和PCB封装一一…...