Python(9):一文学懂进程,线程和协程

news2024/11/26 4:42:01

文章目录

  • 一、进程
    • 1.创建多进程
    • 2.查看进程id
    • 3.进程池
    • 4.进程间的互相通信
  • 二、线程
    • 1.threading线程模块
    • 2.创建多线程
    • 3.互斥锁
    • 4.死锁
    • 5.线程间的互相通信
  • 三、协程
    • 1.认识协程
    • 2.gevent模块在爬虫中的应用
  • 四、多线程、多进程、协程的区别

分类定义
程序一个应用可以当做一个程序,比如qq软件。
进程程序运行的最小的资源分配单位,一个程序可以有多个进程。每个进程中至少有一个线程
线程cpu最小的调度单位,必须依赖进程而存在。
协程又称微线程、纤程。是一种比线程更加轻量级的并发方式,它不需要线程上下文切换的开销,可以在单线程中实现并发

一、进程

  • 多进程是指在同一台计算机中同时运行多个独立的进程。每个进程都有自己的地址空间,可用于执行一些特定的任务。这些进程可以同时执行,从而提高了程序的性能和效率。多进程可以在多核计算机上实现真正的并行计算,可以同时运行多个程序,从而大大提高了计算机的利用率。
  • 在多进程编程中,进程之间是独立的,它们各自运行自己的程序,有自己的地址空间和系统资源,不会相互干扰。每个进程都有自己的PID(进程标识符),它是一个唯一的标识符,用于在系统中识别该进程。多进程可以通过IPC(进程间通信)来实现数据共享和通信。
  • Python的多进程模块multiprocessing提供了一种方便的方法来创建和管理多个进程。它可以在单个Python解释器中创建多个进程,从而实现并行计算。此模块还提供了一些方便的功能,如进程池、进程通信和共享内存等。
  • 多进程编程可以在很多场景中提高程序的运行效率,如CPU密集型任务、I/O密集型任务、并行计算、大规模数据处理和机器学习等。但需要注意多进程之间的数据同步和通信,以避免数据竞争和死锁等问题。

1.创建多进程

①方式1:

import time
from multiprocessing import Process # 进程模块

def sing():
    for i in range(1, 6):
        print('我在唱歌第{}句歌词'.format(i))
        time.sleep(1)

def dance():
    for i in range(1, 6):
        print('我在跳舞第{}段舞蹈'.format(i))
        time.sleep(1)


if __name__ == '__main__':
    t1 = Process(target=sing)  # ------------target=函数名
    t2 = Process(target=dance)
    t1.start()  # -------------------------启动该进程
    t2.start()

方式②:进程类创建

import os
import time
from multiprocessing import Process


class SubProcess(Process):
    def __init__(self, x):
        super().__init__()
        self.x = x

    def run(self):  # 将父类的run函数重写,进程启动时候调用此方法
        for i in range(3):
            print('启动进程', i, os.getpid())
            time.sleep(1)


if __name__ == '__main__':
    p = SubProcess(3)
    p.start()
    p1 = SubProcess(3)
    p1.start()
    
    '''
    启动进程 0 22064
    启动进程 0 10628
    启动进程 1 22064
    启动进程 1 10628
    启动进程 2 22064
    启动进程 2 10628
    '''

2.查看进程id

import os
from multiprocessing import Process

def fun1(n):
    print('参数为', n, '进程id为', os.getpid(), '父进程id为', os.getppid())  # 主进程id使用getppid方法


def fun2(n):
    print('参数为', n, '进程id为', os.getpid(), '父进程id为', os.getppid())  # 子进程采用getpid方法


if __name__ == '__main__':
    print('父进程:', os.getpid())
    t1 = Process(target=fun1, args=(32,))
    t2 = Process(target=fun2, args=(3232432,))  # 传入参数时候是按元组方式进行传入
    t1.start()
    t2.start()
    
    """
    父进程: 22624
    参数为 32 进程id为 23272 父进程id为 22624
    参数为 3232432 进程id为 11100 父进程id为 22624
    """

3.进程池

1.为什么使用进程池

由于进程启动的开销比较大,使用多进程的时候会导致大量内存空间被消耗。为了防止这种情况发生可以使用进程池,(由于启动线程的开销比较小,所以不需要线程池这种概念,多线程只会频繁得切换cpu导致系统变慢,并不会占用过多的内存空间)

2.进程池中常用方法

函数名作用
apply()同步执行(串行)
apply_async()异步执行(并行)
join()主进程等待所有子进程执行完毕。必须在close或terminate()之后。
close()等待所有进程结束后,才关闭进程池。

3.举例使用

from multiprocessing import Pool
import time


def downLoad(movie):
    for i in range(5):
        print(movie, '下载进度%.2f%%' % ((i + 1) / 5 * 100))
        time.sleep(1)
    return movie


def alert(name):
    print(name, '下载完成!')


if __name__ == '__main__':
    movies = ['蔡徐坤吃饭视频', '蔡徐坤洗澡视频', '蔡徐坤化妆视频', '蔡徐坤擦口红视频']
    p = Pool(2)
    for movie in movies:
        p.apply_async(downLoad, args=(movie,), callback=alert)  # -----------callback函数回调,执行完第一个函数毁掉
    p.close()
    p.join()  # ----------------------------使主进程堵塞知道p进程执行完毕

    """
    蔡徐坤吃饭视频 下载进度20.00%
    蔡徐坤洗澡视频 下载进度20.00%
    蔡徐坤吃饭视频 下载进度40.00%
    蔡徐坤洗澡视频 下载进度40.00%
    蔡徐坤洗澡视频 下载进度60.00%
    蔡徐坤吃饭视频 下载进度60.00%
    蔡徐坤洗澡视频 下载进度80.00%
    蔡徐坤吃饭视频 下载进度80.00%
    蔡徐坤吃饭视频 下载进度100.00%
    蔡徐坤洗澡视频 下载进度100.00%
    蔡徐坤化妆视频 下载进度20.00%
    蔡徐坤洗澡视频 下载完成!
    蔡徐坤擦口红视频 下载进度20.00%
    蔡徐坤吃饭视频 下载完成!
    蔡徐坤擦口红视频 下载进度40.00%
    蔡徐坤化妆视频 下载进度40.00%
    蔡徐坤擦口红视频 下载进度60.00%
    蔡徐坤化妆视频 下载进度60.00%
    蔡徐坤化妆视频 下载进度80.00%
    蔡徐坤擦口红视频 下载进度80.00%
    蔡徐坤化妆视频 下载进度100.00%
    蔡徐坤擦口红视频 下载进度100.00%
    蔡徐坤化妆视频 下载完成!
    蔡徐坤擦口红视频 下载完成!
    """

4.进程间的互相通信

进程之间是各个独立的,要是进程与进程之间需要互相使用变量或者空间等等该怎么操作呢?方式有很多,这里我们使用Python的Queue队列(是一种先进先出的存储数据结构)

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-bucs01Wn-1625565694323)(C:\Users\qinfan\AppData\Roaming\Typora\typora-user-images\image-20210706175852363.png)]

举例:

from multiprocessing import Queue

a=Queue() # 队列的最大容纳量
a.put(1)#传入数据
a.put(2)
a.put(3)

while a.qsize()>0:#求出队列现在含有数据的长度,每取出一个数据长度会-1
    print(a.get(),end=' ')#取出数据

# empty和full方法

b=Queue(3)
print(b.empty())#队列空返回True
b.put(1)
b.put(1)
b.put(1)
print(b.full())#队列满返回True
结果:1 2 3 True True

二、线程

  • 线程是程序中一个更加轻量级的多路径执行实现方式。在系统级别,程序会根据系统为其提供的执行时间以及其他程序需要的执行时间统一调度执行。在程序内部,存在承载着不同任务的一个或多个同时或近乎同时执行的线程。实际上系统本身会管理线程的执行,调度其运行在某个核心上或在其他线程需要执行的时候强制中段该线程的执行。
  • 在非并发程序中,只有一个线程的执行。这个线程开始和结束于程序的main函数,并由一个接一个的不同方法或函数来实现程序的全部行为。相比之下,支持并发的程序可以启动一个线程,并按照需求增加线程以创建额外的执行路径。每一条新路径都有独立的自定义启动入口,在程序的main函数中独立运行代码。
  • 而多线程是指操作系统在单个进程内支持多个并发执行路径的能力,可以提高cpu的利用率。
  • python中使用threading模块来操作多线程。

1.threading线程模块

该模块的一些常见方法

函数名作用
threading.currentThread()返回当前的线程变量
threading.enumerate()返回一个包含正在运行的现成的list
threading.activeCount ()求正在运行的线程的列表长度

2.创建多线程

import threading

t=threading.Thread(
	target= 函数名,  # 需要执行的函数的名字
    args=(参数1,参数2...),  #  上述函数的参数,是个元组,无参数可不写,单个参数记住加,
    callback=回调函数 # 不需要回调可不写
)

t.start()  # 线程启动

①创建方式1:

import threading
import time


def sing():
    for i in range(1, 6):
        print('我在唱歌第{}句歌词'.format(i))
        time.sleep(1)


def dance():
    for i in range(1, 6):
        print('我在跳舞第{}段舞蹈'.format(i))
        time.sleep(1)


if __name__ == '__main__':
    t1 = threading.Thread(target=sing)
    t2 = threading.Thread(target=dance)
    t1.start()
    t2.start()

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-GmPdsJ20-1625565694321)(C:\Users\qinfan\AppData\Roaming\Typora\typora-user-images\image-20210706171538461.png)]

②创建方式2:通过线程类

使用线程类的方法,需要继承父类的init方法,这里有几种继承方式

 调用父类的init方法有两种:

#方法1:
super().init()

#方法2:
super(MyThread, self).__init__() 

#方法3:
threading.Thread.__init__(self)

然后我们需要重写run方法,最后生成多个类对象,然后start调用

# -*- coding: utf-8 -*-
import time
from threading import Thread


class Test(Thread):
    def __init__(self):
        super().__init__()

    def sing(self):
        time.sleep(3)
        print('鸡你太美~~~~~')

    def run(self):
        self.sing()


if __name__ == '__main__':
	# 五个线程
    for i in range(5):
        Test().start()

该程序运行后,理论上五次循环需要15s左右,但是使用该线程类,会实现并发运行,约等于3s左右运行

3.互斥锁

多个线程对公有变量处理时,容易造成数据的混乱,造成线程不安全

1.概念

互斥锁:当多个线程几乎同时修改某一个共享数据的时候,需要进行同步控制。线程同步能够保证多个线程安全访问“竞争资源”,最简单的同步机制就是引用互斥锁。互斥锁为资源引入一个状态:锁定/非锁定状态。某个线程要更改共享数据时,先将其锁定,此时资源状态为“锁定”,其它线程不能更改;直到当前线程释放资源,将资源变成"非锁定"状态,其它的线程才能再次锁定该资源。互斥锁保证了每次只有一个线程进行“写操作”,从而保证多个线程数据正确性。

2.锁的好处

  • 确定了某段代码只能由一个线程从头到尾完整地执行。
  • 保证全局变量的安全

3.锁的坏处

  • 阻止了多线程的并发执行,包含锁的某段代码实际上只能以单线程模块执行,效率大大地下降了。
  • 由于可以存在多个锁,不同的线程持有不同的锁,并试图获取对方持有的锁的时,可能会造成“死锁”。

3.使用步骤

from threading import Lock
l=LOCK()  #创建方式

l.acquire()# 上锁方式
l.release()#-解锁方式

4.使用举例

举例:假设两个线程t1和t2都要对num进行+1操作,t1和t2都各自对num修改10次,num最终的值应该为20。紧接着我们把10次改为100000000次,由于多线程并发访问,有可能产生不一样的结果。
代码示例:

from threading import Thread

g_num = 0
def test1():
    global g_num
    for i in range(1000000):
        # g_num += 1
        b = g_num + 1
        g_num = b
    print("---test1---g_num=%d"%g_num)

def test2():
    global g_num
    for i in range(1000000):
        a = g_num + 1
        g_num = a
    print("---test2---g_num=%d"%g_num)
if __name__ == '__main__':
    p1 = Thread(target=test1)
    p1.start()
    p2 = Thread(target=test2)
    p2.start()


#--------------------运行结果------------------------
---test2---g_num=1559989

---test1---g_num=1516811

生成结果和我们预想的不一样,是因为没有控制多个线程对同一资源的访问,对数据造成破坏,使得线程运行的结果达不到预期。这种现象我们称为“线程不安全”,想要解决这种问题,我们使用互斥锁:

  • (1)p1被调用的时候,获取g_num=0,然后上一把锁,即不允许其它线程操作num。
  • (2)对num进行加1
  • (3)解锁,g_num = 1,其它的线程就可以使用g_num的值,而且g_num的值是而不是原来的0
  • (4)同理其它线程在对num进行修改时,都要先上锁,处理完成后再解锁。在上锁的整个过程中,不允许其它线程访问,保证了数据的正确性。

加入互斥锁后的代码:

import threading,time

g_num = 0#全局变量
def w1():
    global g_num
    for i in range(10000000):       
        mutexFlag = mutex.acquire(True)#上锁
        if mutexFlag:
            g_num+=1 
            mutex.release()#解锁
    print("test1---g_num=%d"%g_num)

def w2():
    global g_num
    for i in range(10000000):
        mutexFlag = mutex.acquire(True)# 上锁
        if mutexFlag:
            g_num+=1
            mutex.release()# 解锁
    print("test2---g_num=%d" % g_num)

if __name__ == "__main__":
   mutex = threading.Lock()#创建锁
   t1 = threading.Thread(target=w1)
   t1.start()
   t2 = threading.Thread(target=w2)
   t2.start()

4.死锁

1.死锁的产生

在线程间共享多个资源的时候,如果两个线程分别占有一部分资源并且同时等待对方的资源时,就会造成死锁。尽管死锁很少发生,但一旦发生就会造成应用的停止响应。常见有这两种情况:

  1. 同一个线程先后两次调用lock,在第二次调用时,由于锁已经被自己占用,该线程会挂起等待自己释放锁,由于该线程已被挂起而没有机会释放锁,因此 它将一直处于挂起等待状态,变为死锁;
  2. 线程A获得了锁1,线程B获得了锁2,这时线程A调用lock试图获得锁2,结果是需要挂起等待线程B释放锁2,而这时线程B也调用lock试图获得锁1,结果是需要挂起等待线程A释放锁1,于是线程A和B都在等待对方释放自己才释放,从而造成两个都永远处于挂起状态,造成死锁。

2.避免死锁发生

要最大可能地避免、预防和解除死锁。需要在系统设计、线程调度等方面注意如何确定资源的合理分配算法,避免线程永久占据系统资源。此外,也要防止线程在处于等待状态的情况下占用资源,在系统运行过程中,对线程发出的每一个系统能够满足的资源申请进行动态检查,并根据检查结果决定是否分配资源,若分配后系统可能发生死锁,则不予分配,否则予以分配。因此,对资源的分配要给予合理的规划。

5.线程间的互相通信

  • 关于线程与线程之间可以借助from queue import Queue来实现,此处不举例。
  • 进程与进程,线程与线程之间的通信借助队列来实现,一般常常配合redis来实现。

三、协程

1.认识协程

线程和协程都是实现并发编程的方式,但它们有一些不同的特点和应用场景。协程通常具有以下特点:

  • 协程中的代码可以暂停执行,并且在需要的时候可以恢复执行。
  • 多个协程可以在同一线程中并发执行,但是任意时刻只有一个协程在执行。
  • 协程通常是基于事件循环(Event Loop)实现的,事件循环负责调度协程的执行。

线程的并发编程通常会受到多线程竞争、死锁、上下文切换等问题的限制。在 Python 中,使用多线程编程需要注意线程安全、GIL等问题。而协程是一种轻量级的并发方式,它是在用户空间中实现的,并不依赖于操作系统的调度。协程可以在同一个线程中实现并发,不需要进行上下文切换,因此执行效率非常高。协程通常使用事件循环(Event Loop)来调度协程的执行,事件循环会在协程需要等待 IO 操作或者其他协程时,暂停当前协程的执行,执行其他协程,从而实现并发执行的效果。

协程在Python中历经了多次完善:

  • Python2.x 对协程的支持比较有限,通过 yield 关键字支持的生成器实现了一部分协程的功能但不完全
  • 第三方库 gevent 对协程有更好的支持。
  • Python3.4 中提供了 asyncio 模块。
  • Python3.5 中引入了 async/await 关键字。
  • Python3.6 中 asyncio 模块更加完善和稳定。
  • Python3.7 中内置了 async/await 关键字。

协程的应用场景:

  • 网络编程:协程可以帮助我们实现高并发的网络应用。
  • 异步IO:协程可以帮助我们高效地处理异步IO操作。
  • 数据库操作:协程可以帮助我们实现高并发的数据库应用。
  • 任务调度:协程可以帮助我们实现高效的任务调度系统。

2.gevent模块在爬虫中的应用

由于使用协程的方式有很多种,这里我使用gevent写一个爬虫demo

gevent模块简介

  • Python通过yield提供了对协程的基本支持,但是不完全。而第三方的gevent为Python提供了比较完善的协程支持。
  • gevent是第三方库,通过greenlet实现协程,其基本思想是: 当一个greenlet遇到IO操作时,比如访问网络,就自动切换到其他的greenlet,等到IO操作完成,再在适当的时候切换回来继续执行。由于IO操作非常耗时,经常使程序处于等待状态,有了gevent为我们自动切换协程,就保证总有greenlet在运行,而不是等待IO。
  • 使用gevent,可以获得极高的并发性能,但gevent通常只能在Unix/Linux下运行,在Windows下不保证正常安装和运行。

安装和依赖
依赖于greenlet 、library,支持python 2.6+ 、python 3.3+

pip install gevent

gevent协程爬虫示例

# coding: utf-8 
# 在导入其他库和模块前,先把monkey模块导入进来,并运行monkey.patch_all()。这样,才能先给程序打上补丁。
from gevent import monkey  # 从gevent库里导入了monkey模块,这个模块能将程序转换成可异步的程序

monkey.patch_all()  # 它的作用其实就像你的电脑有时会弹出“是否要用补丁修补漏洞或更新”一样。它能给程序打上补丁,让程序变成是异步模式,而不是同步模式。它也叫“猴子补丁”。
import gevent
import requests
import time


def get_response(url):  # 定义一个函数,用来执行解析网址和爬取内容
    headers = {
        'User-Agent': 'Mozilla/5.0 (Windows NT 6.1; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/75.0.3770.142 Safari/537.36'}

    res = requests.get(url, headers=headers)  # 发出请求
    print(res.status_code)


if __name__ == '__main__':
    start = time.time()  # 开始时间
    # 构建100个请求任务
    url_list = []
    for i in range(100):
        url = 'https://www.baidu.com/'
        url_list.append(url)
    # 使用协程
    tasks_list = []
    for url in url_list:
        # 用gevent.spawn()创建任务,此任务可以调用cra(url)函数,参数1函数名,后边为该函数需要的参数,按顺序写
        task = gevent.spawn(get_response, url)
        tasks_list.append(task)  # 将任务加入列表
    # 调用gevent库里的joinall方法,能启动执行tasks_list所有的任务。
    gevent.joinall(tasks_list)

    end = time.time()  # 结束时间
    print(end - start)

另外我们可以配合多进程+协程使用,可以更快提速。

四、多线程、多进程、协程的区别

优点缺点
多进程1.每个进程互相独立,不影响主程序的稳定性,子进程崩溃没关系;
2. 通过增加CPU,就可以容易扩充性能;
3.每个子进程都有2GB地址空间和相关资源,总体能够达到的性能上限非常大 。
1.逻辑控制复杂,需要和主程序交互;
2.需要跨进程边界,如果有大数据量传送,就不太好,适合小数据量传送、密集运算 多进程调度开销比较大。
多线程1.所有线程可以共享内存和变量
2.线程的执行开销比进程小。
1.每个线程与主程序共用地址空间,受限于2GB地址空间;
2. 线程之间的同步和加锁控制比较麻烦;
3.一个线程的崩溃可能影响到整个程序的稳定性;
协程(相比于线程)1.更加轻量级,占用的资源更少;
2.不需要进行上下文切换,执行效率更高;
3.可以使用事件循环进行调度,实现高并发的效果;
4.不会受到 GIL 的限制。
1.无法利用多核 CPU
2.调试困难等问题。。

在实际开发中,选择的并发方式从具体实际开发来进行选择。最好是采用结合的方式,来进行并发提速。

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.coloradmin.cn/o/1587812.html

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

广佛站点导航助手小程序产品使用说明书

一、产品简介 广佛站点导航助手小程序是一款专为广佛地区用户设计的地铁导航工具。通过获取用户的实时位置信息,小程序能够迅速定位并展示离用户最近的三个地铁站点。用户可以通过本小程序轻松查找地铁站点,规划出行路线,提高出行效率。 二、…

STM32 串口接收定长,不定长数据

本文为大家介绍如何使用 串口 接收定长 和 不定长 的数据。 文章目录 前言一、串口接收定长数据1. 函数介绍2.代码实现 二、串口接收不定长数据1.函数介绍2. 代码实现 三,两者回调函数的区别比较四,空闲中断的介绍总结 前言 一、串口接收定长数据 1. 函…

计算机网络——网络地址转换(NAT)技术

目录 前言 前篇 引言 SNAT(Source Network Address Translation)源网络地址转换 SNAT流程 确定性标记 DNAT(Destination Network Address Translation,目标网络地址转换) NAT技术重要性 前言 本博客是博主用于…

Docker学习笔记(二):在Linux中部署Docker(Centos7下安装docker、环境配置,以及镜像简单使用)

一、前言 记录时间 [2024-4-6] 前置文章:Docker学习笔记(一):入门篇,Docker概述、基本组成等,对Docker有一个初步的认识 在上文中,笔者进行了Docker概述,介绍其历史、优势、作用&am…

Linux:软件包管理器 - yum

Linux:软件包管理器 - yum Linux的软件安装方式源代码安装rpm包安装yum安装 yum三板斧yum listyum installyum remove yum生态yum源 Linux的软件安装方式 源代码安装 在Linux下安装软件, 一个通常的办法是下载到程序的源代码, 并进行编译, 得到可执行程序 源代码安…

python+Flask+django企业仓库进销存管理信息系统35wiz

Flask提供了更大的灵活性和简单性,适合小型项目和微服务。Django则提供了更多的内置功能,适合大型项目。Flask让开发者更多的控制其组件,而Django则遵循开箱即用的原则 本课题使用Python语言进行开发。代码层面的操作主要在PyCharm中进行&am…

五大背景图光线手法,附大量案例,可下载原图。

2023-10-01 23:05贝格前端工场 在背景图设计中,使用光线效果可以帮助营造科技感。以下是一些使用光线来表现科技感的设计技巧: 线性光源:使用线性光源可以在背景图中创建出明亮的光线效果。可以在设计中添加一条或多条线性光源,然…

Docker Nginx 部署Vue项目

先弄个ngix镜像,还原到linux里面 发布包放的位置 nginx配置文件 server {listen 8049;server_name localhost;#charset koi8-r;access_log /var/log/nginx/host.access.log main;error_log /var/log/nginx/error.log error;location / {# root 根目录&a…

全速前进:2024年MAD(机器学习,人工智能和数据)前景(20000字长文)

THE 2024 MAD (MACHINE LEARNING, ARTIFICIAL INTELLIGENCE & DATA) LANDSCAPE 是FirstMark对数据、分析、机器学习和人工智能生态系统的第十次年度展望和「现状」描述 。 在这个领域的10多年里,事情从来没有像今天这样令人兴奋和充满希望。我们多年来描述的所…

[STM32+HAL]DengFOC移植之闭环位置控制

一、源码来源 DengFOC官方文档 二、HAL库配置 1、开启硬件IIC低速模式 低速更稳定 2、PWM波开启 三、keil填写代码 1、AS5600读取编码器数值 #include "AS5600.h" #include "math.h"float angle_prev0; int full_rotations0; // full rotation trac…

Day37|贪心算法part06:738.单调递增的数字、968. 监控二叉树、贪心总结

738. 单调递增的数字 总体思想就是从后往前遍历,比较第i位和第i1位的大小,不符合顺序char[i]减1,i1位填9,找到需要填9的最先位置,然后填9。 class Solution {public int monotoneIncreasingDigits(int n) {String s …

负荷预测 | Matlab基于TCN-BiGRU-Attention单输入单输出时间序列多步预测

目录 效果一览基本介绍程序设计参考资料 效果一览 基本介绍 1.Matlab基于TCN-BiGRU-Attention单输入单输出时间序列多步预测; 2.单变量时间序列数据集,采用前12个时刻预测未来96个时刻的数据; 3.excel数据方便替换,运行环境matlab…

领域驱动设计(DDD)学习心得

领域驱动设计(DDD) 领域驱动设计是一种用于复杂软件系统的软件开发方法论。它强调与软件所服务的业务领域的专家紧密合作,通过深入理解业务领域的复杂性,来构建能够反映业务逻辑的领域模型。DDD的核心思想包括以下几点&#xff1…

你会写SAP技术规格说明书(Specification)吗

有些小伙伴可能还在发愁技术规则说明书应该写什么,做了张思维导图,包含了所有RICEFW。 R - Report - 报表 I - Interface - 接口 C - Conversion - 数据转换 E - Enhancement - 增强 F - Form - 表单 W - Workflow - 工作流

深度学习基础之一:机器学习

文章目录 深度学习基本概念(Basic concepts of deep learning)机器学习典型任务机器学习分类 模型训练的基本概念基本名词机器学习任务流程模型训练详细流程正、反向传播学习率Batch size激活函数激活函数 sigmoid激活函数 softmax & tanh(双曲正切)激活函数 ReLU激活函数 …

深耕国际舞台丨拓数派受邀参与美国 Postgres Conference 2024

在北美地区备受瞩目 Postgres Conference 2024 大会将于4月17日在美国 San Jose 希尔顿举行。拓数派作为立足中国的高科技创新企业,也同时致力于国际开源技术和生态的深耕。本次美国 Postgres Conference 2024 大会中,拓数派将作为黄金赞助商&#xff0c…

Qt | 信号与槽 原理、连接、断开(面试无忧)

1、信号和槽是用于对象之间的通信的,这是 Qt 的核心。为此 Qt 引入了一些关键字,他们是slots、signals、emit,这些都不是 C++关键字,是 Qt 特有的,这些关键字会被 Qt 的 moc转换为标准的 C++语句。 2、Qt 的部件类中有一些已经定义好了的信号和槽,通常的作法是子类化部件…

Vue 工程化开发入门

目录 1. 工程化开发 1.1. 工程化开发模式介绍 1.2. 工程化开发模式下出现的问题: 1.3. 工程化开发模式下出现的问题的解决方法 2. 脚手架Vue CLI 2.1. 脚手架Vue CLI 基本介绍 2.2. 脚手架Vue CLI 的好处 2.3. 脚手架Vue CLI 的使用步骤 2.4. 脚手架目录文…

从大量数据到大数据,King’s SDMS仪器数据采集及科学数据管理系统的应用

对于实验室或检测机构,仪器设备是所有业务开展的基础,数据则是核心命脉,而传统的仪器设备原始数据收集方式,效率低耗时长、操作流程不规范、不易保存与查找、错误率高、易篡改等成了制约检测机构持续高速发展的瓶颈和弊端&#xf…

在线课程平台LearnDash评测 – 最佳 WordPress LMS插件

在我的LearnDash评测中,我探索了流行的 WordPress LMS 插件,该插件以其用户友好的拖放课程构建器而闻名。我深入研究了各种功能,包括课程创建、测验、作业、滴灌内容、焦点模式、报告、分析和管理工具。 我的评测还讨论了套餐和定价选项&…