IO 多路复用技术:原理、类型及 Go 实现

文章目录

    • 1. 引言
      • IO 多路复用的应用场景与重要性
      • 高并发下的 IO 处理挑战
    • 2. IO 多路复用概述
      • 什么是 IO 多路复用
      • IO 多路复用的优点与适用场景
    • 3. IO 多路复用的三种主要实现
      • 3.1 `select`
      • 3.2 `poll`
      • 3.3 `epoll`
      • 三者对比
    • 4. 深入理解 epoll
      • 4.1 epoll 的三大操作
      • 4.2 epoll 的核心数据结构
      • 4.3 边缘触发与水平触发模式详解
        • 1. 水平触发(LT)
        • 2. 边缘触发(ET)
      • 4.4 水平触发和边缘触发的对比
    • 5. Go 实现一个简单的聊天室服务器
      • 5.1 实现思路
      • 5.2 使用 `epoll` 管理客户端连接
      • 5.3 代码实现
      • 5.4 代码解析
      • 5.5 边缘触发的关键点
      • 5.6 流程图:事件处理流程
    • 6. 总结
    • 结束语


1. 引言

IO 多路复用的应用场景与重要性

在网络编程中,服务器需要同时处理多个客户端的请求,这在高并发环境中尤为突出。举例来说,大型即时通讯应用、HTTP 服务器、数据库服务等场景下,往往要支持成千上万个客户端的连接。如果每个客户端连接都使用一个独立的线程,系统资源消耗会极为庞大,并导致频繁的线程切换,严重影响性能。

IO 多路复用技术则是一种可以在单线程中管理多个 IO 事件的高效机制。通过在单线程中监控多个文件描述符(通常是 socket)上的 IO 操作状态,服务器能够灵活处理多个客户端的请求,避免了线程和资源的大量开销。因此,IO 多路复用广泛应用于高并发服务器编程中,如 Nginx、Redis、Kafka 等项目中。


高并发下的 IO 处理挑战

高并发场景中,服务器面临的主要挑战包括:

  • 资源管理难度:大量线程带来的 CPU 资源开销和内存管理负担。
  • 性能瓶颈:传统阻塞 IO 导致的频繁等待,降低了系统的吞吐量。
  • 复杂的事件处理:如何在不增加复杂度的前提下高效管理多客户端连接。

IO 多路复用技术通过提供非阻塞、集中管理的方式有效解决了以上问题。理解 IO 多路复用技术及其实现原理,尤其是高效的 epoll,对网络编程的开发者非常重要。


2. IO 多路复用概述

什么是 IO 多路复用

IO 多路复用是一种在一个线程中同时监听多个 IO 事件的方法。当任何一个文件描述符上有数据可读或可写时,IO 多路复用会通知应用程序去处理该事件。常见的 IO 多路复用包括 selectpollepoll

IO 多路复用的优点与适用场景

  • 高效资源利用:一个线程管理多个连接,减少了多线程的切换开销。
  • 灵活性:可以动态增加或减少监控的文件描述符,适应高并发需求。
  • 提高吞吐量:通过减少阻塞等待,提高了数据处理的整体吞吐量。

适用场景:IO 多路复用特别适合长连接、大量连接、并发请求频繁的场景,如 Web 服务器、聊天室服务、数据库服务等。


3. IO 多路复用的三种主要实现

在 Linux 系统中,IO 多路复用有三种实现方式:selectpollepoll。这三种方法在原理、性能和适用场景上存在显著差异。

3.1 select

select 是最早的 IO 多路复用实现,广泛支持于多种操作系统。

select 的基本工作方式是通过文件描述符集合来管理多个 IO 通道。应用程序在调用 select 时,需要传入一个文件描述符集合(如读集合、写集合和异常集合),select 会阻塞并等待其中任何一个文件描述符的状态发生变化。如果有 IO 事件发生,select 返回相应的描述符集合,供程序进一步处理。

特点:

  • 文件描述符限制select 受文件描述符数量的限制,通常 FD_SETSIZE 设定为 1024,意味着 select 最多只能同时监控 1024 个文件描述符。
  • 性能问题select 在每次调用时都会遍历整个文件描述符集合,检查是否有事件发生,因此性能较低,尤其在高并发场景中,效率更低。

适用场景:由于性能瓶颈和文件描述符限制,select 适用于小规模并发的网络应用,通常是一些简单的服务或学习用途。


3.2 poll

poll 是对 select 的改进,消除了文件描述符数量限制,并提高了一定的性能。

基本原理:与 select 类似,poll 通过遍历文件描述符集合来检查 IO 事件,但它使用链表来存储文件描述符,因此文件描述符的数量不再受 FD_SETSIZE 限制,理论上可以监控更多的连接。

特点:

  • 消除了文件描述符上限poll 允许监控大量的文件描述符。
  • 性能问题仍然存在:与 select 类似,poll 每次调用仍需遍历整个文件描述符集合,因此在大并发场景下效率较低。

适用场景:适合中等规模的并发网络应用,但在高并发环境下,性能依然有限。


3.3 epoll

epoll 是 Linux 提供的高效 IO 多路复用方式,专门为大规模并发场景而设计。

epoll 的工作方式与 selectpoll 有显著不同epoll 使用事件通知机制,即在文件描述符有状态变化时,epoll 将只返回变化的文件描述符,而不是遍历整个文件描述符集合。

epoll 提供了以下三种核心操作:

  1. epoll_create:创建一个 epoll 实例,用于管理多个文件描述符。
  2. epoll_ctl:添加、修改或删除 epoll 实例中的文件描述符事件。
  3. epoll_wait:等待事件触发,并返回已经就绪的事件集合。

epoll 的优势在于:

  • O(1) 复杂度:每次事件触发后,epoll 只返回变化的文件描述符,避免了重复遍历整个集合的开销。
  • 红黑树和就绪链表epoll 使用红黑树存储监控的文件描述符,同时将就绪的事件放入就绪链表,以便高效管理和返回事件。

三者对比

特性selectpollepoll
文件描述符限制受限(通常为 1024 个)无限制无限制
实现方式文件描述符集合链表红黑树 + 就绪链表
性能随并发数增加而降低随并发数增加而降低O(1) 性能
适用场景小规模并发中等规模并发大规模并发,适合高性能场景

通过 epoll,高并发服务器能够在单线程中处理数万个并发连接,因此它被广泛用于各类高性能服务器中。接下来我们将深入探讨 epoll 的核心数据结构和触发机制。


4. 深入理解 epoll

在理解了 epoll 的基本原理后,我们需要深入其核心实现,了解数据结构、触发机制等,尤其是边缘触发和水平触发模式。让我们详细分解 epoll 的工作机制及其在高并发场景中的优势。

4.1 epoll 的三大操作

  1. epoll_create:创建 epoll 实例并初始化相关数据结构。这个实例相当于一个事件管理器,用于集中管理各个文件描述符的事件状态。

    epollFD, err := syscall.EpollCreate1(0)
    if err != nil {panic(err)
    }
    defer syscall.Close(epollFD)
    
  2. epoll_ctlepoll 控制接口,用于向 epoll 实例中添加、修改或删除文件描述符的事件监听。

    event := syscall.EpollEvent{Events: syscall.EPOLLIN, Fd: int32(fd)}
    err := syscall.EpollCtl(epollFD, syscall.EPOLL_CTL_ADD, fd, &event)
    if err != nil {panic(err)
    }
    
  3. epoll_wait:阻塞等待事件触发,并将就绪的文件描述符返回给用户。

    events := make([]syscall.EpollEvent, 10) // 创建事件集合
    nfds, err := syscall.EpollWait(epollFD, events, -1)
    if err != nil {panic(err)
    }
    

4.2 epoll 的核心数据结构

epoll 使用两种关键数据结构来管理和维护文件描述符事件的状态:

  • 红黑树(rbtree):所有被监控的文件描述符存储在红黑树中,以便进行快速查找、插入和删除操作。当我们调用 epoll_ctl 添加或删除文件描述符时,epoll 会操作红黑树。

  • 就绪链表:当某个文件描述符的状态发生变化时,epoll 会将它添加到就绪链表中。每次调用 epoll_wait 时,epoll 会将链表中的就绪事件返回,而不再遍历整个红黑树。

4.3 边缘触发与水平触发模式详解

epoll 中,事件通知有两种模式:水平触发(Level Triggered, LT)边缘触发(Edge Triggered, ET)。两种触发模式决定了 epoll 如何通知应用程序处理 IO 事件。

1. 水平触发(LT)

水平触发是 epoll 的默认模式,也是最常见的触发模式。水平触发模式下,只要文件描述符处于就绪状态(例如,缓冲区中有数据可以读取),epoll_wait 就会不断返回该事件。这意味着应用程序可以多次获取并处理同一个就绪的文件描述符事件,直到事件处理完毕。

流程图:
水平触发

工作机制:

  1. 文件描述符状态变化时触发:每次调用 epoll_wait 时,若文件描述符处于就绪状态,epoll 会将其返回。
  2. 重复通知:如果应用程序没有处理文件描述符的就绪状态(例如,读取完所有数据),epoll_wait 会在下一次调用时再次返回该文件描述符,直到就绪状态被清除(例如,数据被完全读取)。

优缺点:

  • 优点:简单,适合处理大量 IO 事件,因为不会错过任何就绪事件。
  • 缺点:会重复返回同一个事件,增加了系统调用次数,性能可能受影响。

示例:水平触发读取数据

以下是一个使用水平触发读取数据的示例:

func handleEventsLT(epollFD int, events []syscall.EpollEvent, clients map[int]net.Conn) {for _, event := range events {if event.Events&syscall.EPOLLIN != 0 {fd := int(event.Fd)buf := make([]byte, 512)// 循环读取数据直到读完为止for {n, err := clients[fd].Read(buf)if n == 0 || err != nil {// 如果读取完或遇到错误,关闭连接fmt.Printf("Closing connection %d\n", fd)syscall.EpollCtl(epollFD, syscall.EPOLL_CTL_DEL, fd, nil)clients[fd].Close()delete(clients, fd)break}fmt.Printf("Read %d bytes: %s\n", n, string(buf[:n]))// 这里可以处理读取到的数据,比如广播到其他客户端}}}
}

在水平触发模式中,上述代码会在文件描述符仍有未处理的数据时不断被触发,确保我们可以持续读取到数据,直到全部读完。

2. 边缘触发(ET)

边缘触发是一种高效的触发模式,适合性能要求高的场景。与水平触发不同,边缘触发仅在文件描述符状态发生边缘变化时(例如,从不可读到可读)通知一次。也就是说,如果缓冲区有新数据到达,epoll 会通知一次,而不会持续通知。

边缘触发时序图:
边缘触发

工作机制:

  1. 状态变化时触发:边缘触发只在文件描述符的状态从不可读到可读、或不可写到可写时通知一次。
  2. 不重复通知:如果应用程序在收到通知后没有将数据读完,那么在数据再次变化前不会收到新的通知。这意味着应用程序必须一次性将数据全部读取,否则会错过后续的事件通知。

优缺点:

  • 优点:减少了重复通知,性能更高,适合高并发场景。
  • 缺点:开发难度更高。应用程序必须一次性将数据读完,否则可能错过事件通知,导致数据读取不完整。

示例:边缘触发读取数据:

为了在边缘触发模式下保证数据不遗漏,我们通常会使用非阻塞模式,并在单次事件触发中循环读取数据直到缓冲区为空。

func handleEventsET(epollFD int, events []syscall.EpollEvent, clients map[int]net.Conn) {for _, event := range events {if event.Events&syscall.EPOLLIN != 0 {fd := int(event.Fd)buf := make([]byte, 512)// 边缘触发模式下,必须一次性读完所有数据for {n, err := clients[fd].Read(buf)if n == 0 || err != nil {if err != nil && err != syscall.EAGAIN {// 出现非阻塞错误或读到 EOF,关闭连接fmt.Printf("Closing connection %d\n", fd)syscall.EpollCtl(epollFD, syscall.EPOLL_CTL_DEL, fd, nil)clients[fd].Close()delete(clients, fd)}break}fmt.Printf("Read %d bytes: %s\n", n, string(buf[:n]))// 处理读取到的数据}}}
}

在此代码中,循环读取数据直到返回 EAGAIN 错误或数据读完。通过这种方式,确保我们在一次事件触发中尽可能多地读取数据,避免漏掉数据。


4.4 水平触发和边缘触发的对比

特点水平触发(LT)边缘触发(ET)
触发条件只要文件描述符处于就绪状态,持续触发状态从不可用变为可用时触发一次
重复通知会持续返回相同的事件,直到状态改变不会重复通知
性能较低较高
实现复杂度简单较高
适用场景适合一般场景,特别是低并发或对性能要求不高的场景适合高并发场景,性能要求高
  1. 一般场景(水平触发):水平触发模式适合大部分 IO 操作,因为它简单可靠。尤其在低并发场景下,水平触发模式便于实现,不容易遗漏事件。
  2. 高性能场景(边缘触发):在高并发场景下,边缘触发模式具有更高的性能,但要求程序确保数据一次性读取完毕,代码实现较复杂。适用于高性能服务器的设计,比如 Nginx 和 Redis 服务器。

总结来说,水平触发模式适合简单易用的场景边缘触发模式则适用于追求性能的高并发系统。在实际开发中,选择触发模式时应综合考虑系统的并发量、对性能的要求以及代码的复杂度。


5. Go 实现一个简单的聊天室服务器

5.1 实现思路

聊天室服务器的核心功能是管理多个客户端的连接,并支持消息广播。具体而言,这个服务器需要具备以下功能:

  1. 监听客户端连接:通过 epoll 监听客户端的连接请求,并将连接加入 epoll 实例的监听列表中。
  2. 处理客户端消息:在收到某个客户端的消息时,服务器将消息广播给其他所有客户端。
  3. 边缘触发模式下的高效读写:在边缘触发(ET)模式下实现非阻塞读写,保证在一次触发中尽量将数据处理完。

5.2 使用 epoll 管理客户端连接

在实现中,我们将使用 Go 的 syscall 包直接调用 epoll 系统接口。每个新连接或就绪的客户端 socket 会通过 epoll_wait 触发事件,从而被服务器捕获并处理。边缘触发模式要求我们在处理每个 socket 时确保数据被一次性读取完毕,避免遗漏数据。

5.3 代码实现

以下是完整的聊天室服务器代码,实现了客户端连接管理、消息广播和边缘触发模式的高效事件处理。代码详细注释了各个步骤,便于理解 epoll 的具体应用。

package mainimport ("fmt""net""syscall"
)const (MaxEvents = 10
)func main() {// 1. 创建监听 socketlistener, err := net.Listen("tcp", ":8080")if err != nil {panic(err)}defer listener.Close()fmt.Println("Chat server started on :8080")// 2. 创建 epoll 实例epollFD, err := syscall.EpollCreate1(0)if err != nil {panic(err)}defer syscall.Close(epollFD)// 3. 将监听 socket 的文件描述符加入 epoll 实例listenerFD := int(listener.(*net.TCPListener).Fd())addToEpoll(epollFD, listenerFD, syscall.EPOLLIN)// 创建事件列表和客户端连接映射events := make([]syscall.EpollEvent, MaxEvents)clients := make(map[int]net.Conn) // 存储客户端连接,键为文件描述符for {// 4. 等待事件触发n, err := syscall.EpollWait(epollFD, events, -1)if err != nil {panic(err)}// 5. 遍历每个就绪事件for i := 0; i < n; i++ {fd := int(events[i].Fd)if fd == listenerFD {// 处理新的客户端连接conn, err := listener.Accept()if err != nil {fmt.Println("Error accepting connection:", err)continue}clientFD := int(conn.(*net.TCPConn).Fd())addToEpoll(epollFD, clientFD, syscall.EPOLLIN|syscall.EPOLLET) // 使用边缘触发clients[clientFD] = connfmt.Println("New client connected:", clientFD)} else {// 处理来自客户端的数据handleClientMessage(fd, epollFD, clients)}}}
}// addToEpoll 将文件描述符添加到 epoll 实例中,监听指定事件
func addToEpoll(epollFD int, fd int, events uint32) {event := syscall.EpollEvent{Events: events, Fd: int32(fd)}if err := syscall.EpollCtl(epollFD, syscall.EPOLL_CTL_ADD, fd, &event); err != nil {panic(err)}
}// handleClientMessage 读取客户端消息并广播给其他客户端
func handleClientMessage(clientFD int, epollFD int, clients map[int]net.Conn) {buf := make([]byte, 512)conn := clients[clientFD]// 使用边缘触发,循环读取数据直到读取完毕for {n, err := conn.Read(buf)if n == 0 || err != nil {// 客户端断开连接fmt.Printf("Client %d disconnected\n", clientFD)syscall.EpollCtl(epollFD, syscall.EPOLL_CTL_DEL, clientFD, nil)conn.Close()delete(clients, clientFD)break}// 打印并广播消息message := fmt.Sprintf("Client %d: %s", clientFD, string(buf[:n]))fmt.Print(message)broadcastMessage(clientFD, message, clients)}
}// broadcastMessage 将消息广播给其他所有客户端
func broadcastMessage(senderFD int, message string, clients map[int]net.Conn) {for fd, conn := range clients {if fd != senderFD { // 不发送给自己conn.Write([]byte(message))}}
}

5.4 代码解析

  1. 创建监听 socket:使用 net.Listen 启动一个 TCP 监听 socket 以接受客户端连接。
  2. 创建 epoll 实例:通过 syscall.EpollCreate1 创建一个 epoll 实例,返回 epollFD 文件描述符,用于管理多个客户端连接。
  3. 将监听 socket 添加到 epoll 实例:将监听 socket 的文件描述符添加到 epoll,并设置监听 EPOLLIN 事件,表示有新的客户端连接时会触发事件。
  4. 等待事件触发syscall.EpollWait 阻塞等待事件触发,并返回已经就绪的事件列表。
  5. 处理新客户端连接:当监听 socket 的事件触发时,表示有新客户端连接。使用 Accept 接受连接,并将新连接的文件描述符添加到 epoll 中,设置为 EPOLLET 模式(边缘触发)。
  6. 读取客户端消息并广播:当客户端 socket 的事件触发时,表示有消息可读。在 handleClientMessage 中,我们使用非阻塞方式循环读取数据,直到所有数据读取完毕,随后广播消息给其他客户端。
  7. 广播消息broadcastMessage 函数将来自某个客户端的消息广播给所有其他客户端,实现聊天室功能。

5.5 边缘触发的关键点

在边缘触发模式(EPOLLET)下,epoll_wait 只会在文件描述符状态发生变化时触发一次。为确保在一次触发中处理完所有数据,我们在 handleClientMessage 函数中使用非阻塞读取,循环读取直到所有数据读完。这避免了数据遗漏,同时利用边缘触发的高性能。


5.6 流程图:事件处理流程

以下流程图展示了聊天室服务器的事件处理流程,帮助我们直观理解每一步骤:
完成触发流程


6. 总结

在本篇文章中,我们系统深入地讲解了 IO 多路复用技术,从基础概念到具体实现,帮助读者理解其在高并发网络编程中的重要性。以下是我们文章中的关键要点总结:

  1. IO 多路复用的意义与应用场景:我们首先介绍了 IO 多路复用的重要性。通过允许单线程管理多个 IO 通道,IO 多路复用可以极大地提升服务器的并发能力,广泛应用于高性能服务器、实时通讯系统和数据库服务等场景。

  2. 三种 IO 多路复用实现方式:selectpollepoll:我们分别介绍了 selectpollepoll 的基本原理、优缺点以及适用场景。虽然 selectpoll 提供了基本的 IO 多路复用功能,但它们的性能在高并发下存在瓶颈。epoll 则是专为高并发设计的高效 IO 多路复用机制,具备 O(1) 的性能特征,是大型 Linux 服务器应用的主流选择。

  3. 深入理解 epoll 的实现细节:通过讲解 epoll 的三大操作(epoll_createepoll_ctlepoll_wait)、核心数据结构(红黑树和就绪链表)、以及事件触发模式(边缘触发和水平触发),我们详细剖析了 epoll 的高效实现原理。特别是边缘触发模式下的非阻塞处理,帮助我们了解了如何在高并发场景下充分利用 epoll 的性能优势。

  4. 基于 Go 实现的聊天室服务器示例:我们提供了一个简单的聊天室服务器实现,展示了 epoll 的实际应用。通过 epoll 的边缘触发模式,服务器能够高效管理多个客户端连接并进行消息广播。具体代码实现帮助我们理解如何使用 epoll 的非阻塞读写来确保数据处理的完整性。


结束语

IO 多路复用是一项强大的技术,epoll 的高效实现为 Linux 系统中的高并发网络编程提供了有力支持。在本篇文章中,我们通过详细讲解和示例实现,让大家更加深入地理解了 IO 多路复用技术的原理和应用。

祝大家在 IO 多路复用和高并发编程的学习之旅中一帆风顺!如有任何问题或讨论,欢迎留言,我们共同交流。


在这里插入图片描述

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

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

相关文章

大数据新视界 -- 大数据大厂之 Impala 性能优化:从数据压缩到分析加速(下)(8/30)

&#x1f496;&#x1f496;&#x1f496;亲爱的朋友们&#xff0c;热烈欢迎你们来到 青云交的博客&#xff01;能与你们在此邂逅&#xff0c;我满心欢喜&#xff0c;深感无比荣幸。在这个瞬息万变的时代&#xff0c;我们每个人都在苦苦追寻一处能让心灵安然栖息的港湾。而 我的…

信息学奥赛一本通 1395:烦人的幻灯片(slides)

【题目链接】 ybt 1395&#xff1a;烦人的幻灯片(slides) 【题目考点】 1. 图论&#xff1a;拓扑排序 【解题思路】 先理解题意&#xff1a; 如图&#xff0c;每张幻灯片是一个矩形&#xff0c;在该矩形范围内有一个位置写了这张幻灯片的编号。但实际情况是幻灯片是透明…

DB-GPT系列(三):底层大模型设置(开源模型、在线模型)

前面两篇文章分别对 DB-GPT 的总体情况进行了介绍&#xff0c;同时涵盖了镜像一键部署与源码部署这两种部署方式。 DB-GPT系列&#xff08;一&#xff09;&#xff1a;DB-GPT能帮你做什么&#xff1f; DB-GPT系列&#xff08;二&#xff09;&#xff1a;DB-GPT部署&#xff0…

LabVIEW配电产品精度测试系统

开发了一种基于LabVIEW平台的配电产品精度测试系统&#xff0c;通过自动化测试流程实现更高的测试准确性与效率。系统采用串口和TCP通信技术&#xff0c;与多功能交流采样变送器和配电设备无缝数据交互&#xff0c;提升了测试工作的可靠性和一致性。 一、项目背景 在配电产品…

详解Java之Spring MVC篇二

目录 获取Cookie/Session 理解Cookie 理解Session Cookie和Session的区别 获取Cookie 获取Session 获取Header 获取User-Agent 获取Cookie/Session 理解Cookie HTTP协议自身是“无状态”协议&#xff0c;但是在实际开发中&#xff0c;我们很多时候是需要知道请求之间的…

【大数据学习 | kafka】kafka的偏移量管理

1. 偏移量的概念 消费者在消费数据的时候需要将消费的记录存储到一个位置&#xff0c;防止因为消费者程序宕机而引起断点消费数据丢失问题&#xff0c;下一次可以按照相应的位置从kafka中找寻数据&#xff0c;这个消费位置记录称之为偏移量offset。 kafka0.9以前版本将偏移量信…

RabbitMQ 管理平台(控制中心)的介绍

文章目录 一、RabbitMQ 管理平台整体介绍二、Overview 总览三、Connections 连接四、Channels 通道五、Exchanges 交换机六、Queues 队列查看队列详细信息查看队列的消息内容 七、Admin 用户给用户分配虚拟主机 一、RabbitMQ 管理平台整体介绍 RabbitMQ 管理平台内有六个模块&…

推荐一款强大的图像处理软件:Adobe Photoshop2025

AdobePhotoShop赛博日落版是一款强大的图像处理软件&#xff0c;专为用户提供多种先进的图像编辑功能。该版本包含了最新的AI移除工具、AI查找干扰功能以及Neural Filters神经滤镜插件&#xff0c;旨在提升用户的创作效率和成品效果。 主要功能 - AI创意填充&#xff1a;该功能…

PC模块静电放电测试中的宕机黑屏

静电放电(ESD)是电子设备中常见且难以避免的干扰源之一,尤其是在复杂的电子系统中,它对系统的稳定性和可靠性影响极大。近期,在进行静电放电(6KV接触放电、15KV空气放电)测试时,某P C模块在多个端子(如USB、RJ45、HDMI及耳机端子)遭遇了显示黑屏、图像异常及系统宕机…

吴恩达深度学习笔记:卷积神经网络(Foundations of Convolutional Neural Networks)4.7-4.8

目录 第四门课 卷积神经网络&#xff08;Convolutional Neural Networks&#xff09;第四周 特殊应用&#xff1a;人脸识别和神经风格转换&#xff08;Special applications: Face recognition &Neural style transfer&#xff09;4.7 深度卷积网络学习什么&#xff1f;&am…

适用于个人或团队的文档管理和知识库系统,NAS快速部署『BookStack』

适用于个人或团队的文档管理和知识库系统&#xff0c;NAS快速部署『BookStack』 哈喽小伙伴们好&#xff0c;我是Stark-C~ 知识库对于很多需要和文字打交道的个人或者团队都不陌生对吧&#xff1f;对于我们个人来说&#xff0c;它可以将常用的学习资料、工作笔记、项目计划和…

并发编程的基础:深入理解内存屏障(Memory Barriers)

内存屏障是一种基础语言&#xff0c;在不同的计算机架构下有不同的实现细节。本文主要在x86_64处理器下&#xff0c;通过Linux及其内核代码来分析和使用内存屏障 对大多数应用层开发者来说&#xff0c;“内存屏障”&#xff08;memory Barrier&#xff09;是一种陌生&#xff…

Windows转Mac过渡指南

最近由于工作原因开始使用mac电脑&#xff0c;说实话刚拿到手的时候&#xff0c;window党表示真的用不惯。坚持用一下午之后&#xff0c;发现真的yyds&#xff0c;这篇文章说说mac电脑的基本入门指南。 1. 不会使用mac的触摸板&#xff0c;接上鼠标发现滚轮和windows是反的。 …

poi excel数据统计导出

##poi excel导出案例 1.ajxa导出请求没有任何反应&#xff0c;打断点看了workBook中也有数据&#xff0c;网上查阅说ajax请求导出无法接收流&#xff0c;换成location.href,果然可以了 2.控制器代码 response.setCharacterEncoding("UTF-8");response.setContentTyp…

昆仑通态触摸屏-如何完成几个窗口的切换

一、启动窗口 想要哪一个窗口是启动时第一个显示的&#xff0c;就把谁设置为启动窗口就可以。 二、公共窗口 给一个窗口命名为公共窗口 然后选择一个窗口&#xff0c;将他的公共窗口设置为我们刚才命名的那个窗口 三、页面切换 页面切换&#xff0c;是通过在公共窗口内设置按…

dns服务器配置

主服务器 1.挂载点 mount /dev/sr0 /mnt 2.防火墙关闭 systemctl stop firewalld setenforce 0 3.下载bind软件 dnf install bind -y 4.进行正向解析配置 vim /etc/named.conf options { listen-on port 53 { 192.168.92.128; }; directo…

GraphRAG本地部署使用及兼容千帆通义

文章目录 前言一、GraphRAG本地安装1.创建环境并安装2.准备demo数据3.初始化demo目录 二、GraphRAG兼容千帆通义等大模型1.安装 graphrag-more2.准备Demo数据3.初始化demo目录4.移动和修改 settings.yaml 文件 三、知识库构建与使用1.知识库构建2.执行查询 前言 GraphRAG是一种…

揭秘2024年最火的5个科技趋势,你准备好迎接了吗?

在这个信息化飞速发展的时代&#xff0c;科技正以前所未有的速度改变着我们的生活。2024年&#xff0c;科技行业将迎来哪些新的突破与趋势&#xff1f;从人工智能到量子计算&#xff0c;从数字货币到智能家居&#xff0c;未来已来&#xff0c;而我们正站在一个巨变的风口浪尖上…

Python实例:爱心代码

前言 在编程的奇妙世界里,代码不仅仅是冰冷的指令集合,它还可以成为表达情感、传递温暖的独特方式。今天,我们将一同探索用 Python 语言绘制爱心的神奇之旅。 爱心,这个象征着爱与温暖的符号,一直以来都在人类的情感世界中占据着特殊的地位。而通过 Python 的强大功能,…

scala学习记录,Set,Map

set&#xff1a;集合&#xff0c;表示没有重复元素的集合&#xff0c;特点&#xff1a;唯一 语法格式&#xff1a;val 变量名 Set [类型]&#xff08;元素1&#xff0c;元素2...&#xff09; 可变不可变 可变&#xff08;mutable&#xff09;可对元素进行添加&#xff0c;删…