这次一次性通关channel!
一 前言
前几天面试某大厂的云原生岗位,原本是一个轻松+愉快的过程,当问到第二个问题,我就发现事情的不对劲,先复盘一下面试官有关Channel的问题,然后再逐一解决,最后进行扩展,这次一定要一次性通关channel!答应我,看完这篇文章,不要再被Channel吊打了!
面试题
-
介绍一下Channel
-
Channel在go中起什么作用
-
Channel为什么需要两个队列实现
-
Go为什么要开发Channel,而别的语言为什么没有
-
Channel底层是使用锁控制并发的,为什么不直接使用锁
然后我们进行一下扩展,玩转Channel!
-
Channel的底层原理和数据结构
-
Channel的读写流程
-
Channel为什么能做到线程安全
-
操作Channel可能出现的情况
-
Channel有哪些常见的使用场景
-
Channel的读写操作是否是原子性的,如何实现
-
如何避免在Channel中出现死锁的情况
-
Channel可以在多个goroutine之间传递什么类型的数据
-
如何在Channel中使用缓存区
-
在使用Channel时,如何保证数据的同步性和一致性
-
如何保证Channel的安全性
-
Channel的大小是否对性能有影响
-
Channel的内存模型是什么
-
如何在Channel中传递复杂的数据类型
-
Channel和goroutine之间的关系是什么
-
在Go语言中,Channel和锁的使用场景有哪些区别
二 解决面试题
1. 介绍一下Channel
Channel是Go语言中的一种并发原语,用于在goroutine之间传递数据和同步执行。Channel实际上是一种特殊类型的数据结构,可以将其想象成一个管道,通过它可以发送和接收数据,实现goroutine之间的通信和同步。
Channel的特点包括:
- Channel是类型安全的,可以确保发送和接收的数据类型一致。
- Channel是阻塞的,当发送或接收操作没有被满足时,会阻塞当前goroutine,直到满足条件。
- Channel是有缓存的,可以指定缓存区大小,当缓存区已满时发送操作会被阻塞,当缓存区为空时接收操作会被阻塞。
- Channel是可以关闭的,可以使用close()函数关闭Channel,关闭后的Channel不能再进行发送操作,但可以进行接收操作。
Channel的使用方式包括:
- 创建Channel:使用make()函数创建Channel,指定Channel的类型和缓存区大小。
- 发送数据:使用<-运算符将数据发送到Channel中。
- 接收数据:使用<-运算符从Channel中接收数据。
- 关闭Channel:使用close()函数关闭Channel。
2. Channel在go中起什么作用
在 Go 中,channel
是一种用于在 goroutine 之间传递数据的并发原语。channel
可以让 goroutine 在发送和接收操作之间同步,从而避免了竞态条件,从而更加安全地共享内存。
channel
类似于一个队列,数据可以从一个 goroutine 中发送到 channel
,然后从另一个 goroutine 中接收。channel
可以是有缓冲的,这意味着可以在 channel
中存储一定数量的值,而不仅仅是一个。如果 channel
是无缓冲的,则发送和接收操作将会同步阻塞,直到有 goroutine 准备好接收或发送数据。
注:我这里提到了Channel底层用到了两个队列实现。所以就有了下面的问题
3. Channel为什么需要两个队列实现
一个Channel可以被看作是一个通信通道,用于在不同的进程之间传递数据。在具体的实现中,一个Channel通常需要使用两个队列来实现。这两个队列是发送队列和接收队列。
发送队列是用来存储将要发送的数据的队列。当一个进程想要通过Channel发送数据时,它会将数据添加到发送队列中。发送队列中的数据会按照先进先出的顺序被逐个发送到接收进程。如果发送队列已经满了,那么发送进程就需要等待,直到有足够的空间可以存储数据。
接收队列是用来存储接收进程已经准备好接收的数据的队列。当一个进程从Channel中接收数据时,它会从接收队列中取出数据。如果接收队列是空的,那么接收进程就需要等待,直到有新的数据可以接收。
使用两个队列实现Channel的主要原因是为了实现异步通信。发送进程可以在发送数据之后立即继续执行其他任务,而不需要等待接收进程确认收到数据。同样,接收进程也可以在等待数据到达的同时执行其他任务。这种异步通信的实现方式可以提高系统的吞吐量和响应速度。
4. Go为什么要开发Channel,而别的语言为什么没有
在Go语言中,Channel是一种非常重要的并发原语。Go语言将Channel作为语言内置的原语,可能是出于以下几个方面的考虑:
- 并发安全:在多线程并发环境下,使用Channel可以保证数据的安全性,避免多个线程同时访问共享数据导致的数据竞争和锁的开销。
- 简单易用:Go语言中的Channel是一种高度抽象的概念,可以非常方便地实现不同线程之间的数据传输和同步。通过Channel,程序员不需要手动地管理锁、条件变量等底层的同步原语,使得程序的编写更加简单和高效。
- 天然支持并发:Go语言中的Channel与goroutine密切相关,这使得Channel天然地支持并发。程序员可以通过使用Channel和goroutine来实现非常高效的并发编程。
虽然其他编程语言中没有像Go语言中的Channel这样的内置并发原语,但是许多编程语言提供了类似于Channel的实现,比如Java的ConcurrentLinkedQueue、Python的Queue、C++的std::queue等。这些实现虽然没有Go语言中的Channel那么简单易用和高效,但也能够满足多线程编程中的数据传输和同步需求。
注:我这里提到了Channel底层是使用锁实现。所以就有了下面的问题
5. Channel底层是使用锁控制并发的,为什么不直接使用锁
虽然在Go语言中,Channel底层实现是使用锁控制并发的,但是Channel和锁的使用场景是不同的,具有不同的优势和适用性。
首先,Channel比锁更加高级和抽象。Channel可以实现多个goroutine之间的同步和数据传递,不需要程序员显式地使用锁来进行线程间的协调。Channel可以避免常见的同步问题,比如死锁、饥饿等问题。
其次,Channel在语言层面提供了一种更高效的并发模型。在使用锁进行并发控制时,需要程序员自己手动管理锁的获取和释放,这增加了代码复杂度和错误的风险。而使用Channel时,可以通过goroutine的调度和Channel的阻塞机制来实现更加高效和简单的并发控制。
此外,Channel还可以避免一些由锁导致的性能问题,如锁竞争、锁粒度过大或过小等问题。Channel提供了一种更加精细的控制机制,能够更好地平衡不同goroutine之间的并发性能。
总的来说,虽然Channel底层是使用锁控制并发的,但是Channel在语言层面提供了更加高级、抽象和高效的并发模型,可以使程序员更加方便和安全地进行并发编程。
三 扩展面试题
1. Channel的底层原理和数据结构
在Go语言中,Channel是通过一个有缓存的队列来实现的,底层数据结构是一个双向链表。是一个叫做hchan的结构体,每个Channel都有一个send队列和一个receive队列,用于存放发送和接收操作的goroutine。当发送操作和接收操作发生时,它们会被添加到对应的队列中,等待对方的操作来满足条件。
type hchan struct {//channel分为无缓冲和有缓冲两种。//对于有缓冲的channel存储数据,借助的是如下循环数组的结构qcount uint // 循环数组中的元素数量dataqsiz uint // 循环数组的长度buf unsafe.Pointer // 指向底层循环数组的指针elemsize uint16 //能够收发元素的大小closed uint32 //channel是否关闭的标志elemtype *_type //channel中的元素类型//有缓冲channel内的缓冲数组会被作为一个“环型”来使用。//当下标超过数组容量后会回到第一个位置,所以需要有两个字段记录当前读和写的下标位置sendx uint // 下一次发送数据的下标位置recvx uint // 下一次读取数据的下标位置//当循环数组中没有数据时,收到了接收请求,那么接收数据的变量地址将会写入读等待队列//当循环数组中数据已满时,收到了发送请求,那么发送数据的变量地址将写入写等待队列recvq waitq // 读等待队列sendq waitq // 写等待队列lock mutex //互斥锁,保证读写channel时不存在并发竞争问题
}
对于有缓存的Channel,缓存区的大小即为队列的长度,当缓存区已满时,发送操作会被阻塞,直到有接收操作来取走数据;当缓存区为空时,接收操作会被阻塞,直到有发送操作来填充数据。
Channel底层的同步机制是基于等待队列和信号量实现的。每个Channel都维护着一个等待队列,其中包含了所有等待操作的goroutine;同时还维护着一个计数器,用于记录当前缓存区中的元素数量。当发送操作需要等待时,会将当前goroutine添加到等待队列中,并使计数器减一;当接收操作需要等待时,会将当前goroutine添加到等待队列中,并使计数器加一。当有其他操作满足条件时,会从等待队列中取出相应的goroutine,并将其重新加入到可执行队列中,等待调度器的调度。
2. Channel的读写流程
向 channel 写数据:
若等待接收队列 recvq 不为空,则缓冲区中无数据或无缓冲区,将直接从 recvq 取出 G ,并把数据写入,最后把该 G 唤醒,结束发送过程。
若缓冲区中有空余位置,则将数据写入缓冲区,结束发送过程。
若缓冲区中没有空余位置,则将发送数据写入 G,将当前 G 加入 sendq ,进入睡眠,等待被读 goroutine 唤醒。
从 channel 读数据
若等待发送队列 sendq 不为空,且没有缓冲区,直接从 sendq 中取出 G ,把 G 中数据读出,最后把 G 唤醒,结束读取过程。
如果等待发送队列 sendq 不为空,说明缓冲区已满,从缓冲区中首部读出数据,把 G 中数据写入缓冲区尾部,把 G 唤醒,结束读取过程。
如果缓冲区中有数据,则从缓冲区取出数据,结束读取过程。
将当前 goroutine 加入 recvq ,进入睡眠,等待被写 goroutine 唤醒。
关闭 channel
1.关闭 channel 时会将 recvq 中的 G 全部唤醒,本该写入 G 的数据位置为 nil。将 sendq 中的 G 全部唤醒,但是这些 G 会 panic。
panic 出现的场景还有:
- 关闭值为 nil 的 channel
- 关闭已经关闭的 channel
- 向已经关闭的 channel 中写数据
3. Channel为什么能做到线程安全
Channel的线程安全主要是通过其内部的同步机制实现的。
Channel 可以理解是一个先进先出的队列,通过管道进行通信,发送一个数据到Channel和从Channel接收一个数据都是原子性的。不要通过共享内存来通信,而是通过通信来共享内存,前者就是传统的加锁,后者就是Channel。设计Channel的主要目的就是在多任务间传递数据的,本身就是安全的。
当多个goroutine通过Channel进行通信时,Channel会保证每个操作的原子性和顺序性,避免了多个goroutine同时访问共享变量导致的数据竞争问题。Channel的阻塞特性也保证了在发送和接收操作发生时,它们会被添加到等待队列中,直到满足条件后才会被唤醒,从而避免了死锁问题。
4. 操作Channel可能出现的情况
channel存在3种状态:
- nil,未初始化的状态,只进行了声明,或者手动赋值为nil
- active,正常的channel,可读或者可写
- closed,已关闭,千万不要误认为关闭channel后,channel的值是nil
操作 | 一个零值nil通道 | 一个非零值但已关闭的通道 | 一个非零值且尚未关闭的通道 |
---|---|---|---|
关闭 | 产生恐慌 | 产生恐慌 | 成功关闭 |
发送数据 | 永久阻塞 | 产生恐慌 | 阻塞或者成功发送 |
接收数据 | 永久阻塞 | 永不阻塞 | 阻塞或者成功接收 |
5. Channel有哪些常见的使用场景
- 任务分发和处理:可以通过Channel将任务分发给多个goroutine进行处理,并将处理结果发送回主goroutine进行汇总和处理。
- 并发控制:可以通过Channel来进行信号量控制,限制并发的数量,避免资源竞争和死锁等问题。
- 数据流处理:可以通过Channel实现数据流的处理,将数据按照一定的规则传递给不同的goroutine进行处理,提高并发处理效率。
- 事件通知和处理:可以通过Channel来实现事件的通知和处理,将事件发送到Channel中,让订阅了该Channel的goroutine进行相应的处理。
- 异步处理:可以通过Channel实现异步的处理,将任务交给其他goroutine处理,自己继续执行其他任务,等待处理结果时再从Channel中获取。
6. Channel的读写操作是否是原子性的,如何实现
Channel的读写操作是原子性的,并且是由Go语言内部的同步机制来保证的。
当一个goroutine进行Channel的读写操作时,Go语言内部会自动进行同步,保证该操作的原子性和顺序性。这种同步机制主要涉及到两个部分:
- 基于锁的同步:在Channel的底层实现中,使用了一种基于锁的同步机制,它可以保证每个读写操作都是原子性的,避免了多个goroutine同时读写导致的数据竞争问题。
- 基于等待的同步:当一个goroutine进行Channel的读写操作时,如果Channel当前为空或已满,它就会被添加到等待队列中,直到满足条件后才会被唤醒,这种等待的同步机制可以避免因Channel状态不满足条件而导致的死锁问题。
通过这种基于锁和等待的同步机制,Go语言保证了Channel的读写操作是原子性的,可以在多个goroutine之间安全地进行通信和同步。
7. 如何避免在Channel中出现死锁的情况
- 避免在单个goroutine中对Channel进行读写操作:如果一个goroutine同时进行Channel的读写操作,很容易出现死锁的情况,因为该goroutine无法切换到其他任务,导致无法释放Channel的读写锁。因此,在进行Channel的读写操作时,应该尽量将它们分配到不同的goroutine中,以便能够及时切换任务。
- 使用缓冲Channel:缓冲Channel可以在一定程度上缓解读写操作的同步问题,避免因为Channel状态不满足条件而导致的死锁问题。如果Channel是非缓冲的,那么写操作必须等到读操作执行之后才能完成,反之亦然,这种同步会导致程序无法继续执行。而如果使用缓冲Channel,就可以避免这种同步问题,即使读写操作之间存在时间差,也不会导致死锁。
- 使用select语句:select语句可以在多个Channel之间进行选择操作,避免因为某个Channel状态不满足条件而导致的死锁问题。在使用select语句时,应该注意判断每个Channel的状态,避免出现同时等待多个Channel的情况,这可能导致死锁。
- 使用超时机制:在进行Channel的读写操作时,可以设置一个超时时间,避免因为Channel状态不满足条件而一直等待的情况。如果超过一定时间仍然无法读写Channel,就可以选择放弃或者进行其他操作,以避免死锁。
8. Channel可以在多个goroutine之间传递什么类型的数据
在Go语言中,Channel可以在多个goroutine之间传递任何类型的数据,包括基本数据类型、复合数据类型、结构体、自定义类型等。这些数据类型在传递过程中都会被封装成对应的指针类型,并由Channel进行传递。
9. 如何在Channel中使用缓存区
在Go语言中,我们可以使用带缓冲的Channel来实现Channel的缓存区功能。带缓冲的Channel可以存储一定数量的元素,而不必立即将它们交给接收方。这样可以减少发送和接收操作之间的同步,从而提高程序的性能。
使用带缓冲的Channel,可以通过在Channel声明时指定缓冲区的大小来实现。例如,声明一个容量为10的缓冲Channel可以使用以下语句:
ch := make(chan int, 10)
在这个例子中,我们创建了一个整型缓冲Channel,其容量为10。这意味着在Channel中可以存储10个整型元素,而不必立即将它们发送到接收方。当Channel中的元素数量达到缓冲区容量时,再进行写入操作时,写入操作就会被阻塞,直到有接收方读取了Channel中的元素。
10. 在使用Channel时,如何保证数据的同步性和一致性
在使用Channel时,为了保证数据的同步性和一致性,可以采用以下几种方式:
- 合理设计Channel的容量:当Channel容量过小时,容易出现发送者和接收者之间的阻塞,而当容量过大时,可能会出现数据不一致的问题。因此,在设计Channel时,需要根据实际情况合理设定容量大小,以避免数据同步性和一致性的问题。
- 使用互斥锁保证数据访问的互斥性:如果多个goroutine同时对某个共享的数据进行访问,可能会导致数据不一致的问题。此时,可以使用互斥锁来保证数据访问的互斥性,以避免多个goroutine同时对同一份数据进行访问。
- 使用同步机制实现数据同步:在某些情况下,我们可能需要在多个goroutine之间进行数据同步,以确保数据的一致性。此时,可以使用一些同步机制,例如WaitGroup、Barrier、Cond等,来实现数据同步。
11. 如何保证Channel的安全性
- 确保Channel的正确使用:在使用Channel时,需要确保发送和接收操作的正确性。特别是在并发环境下,必须正确处理并发操作,避免出现竞争条件或死锁等问题。因此,在使用Channel时,需要根据实际情况选择合适的同步机制,例如互斥锁、条件变量、原子操作等,以确保Channel的正确使用。
- 避免Channel的泄漏:如果Channel没有被及时关闭,可能会导致资源泄漏和性能问题。因此,在使用Channel时,需要确保及时关闭Channel,避免出现资源泄漏的情况。
- 避免Channel的阻塞:如果Channel的容量较小,可能会导致发送和接收操作的阻塞。此时,可以使用缓冲Channel或者带超时的发送和接收操作,避免Channel的阻塞。
- 避免Channel的死锁:如果多个goroutine之间出现死锁,可能会导致程序的停滞和性能问题。因此,在使用Channel时,需要避免死锁的情况,例如避免循环依赖、避免同时使用多个Channel等。
12. Channel的大小是否对性能有影响
Channel的大小对性能会产生一定的影响。Channel的大小是指Channel可以容纳的元素数量,可以通过在创建Channel时指定容量大小来控制。当Channel的容量较小时,可能会导致发送和接收操作的阻塞,从而影响程序的性能。而当Channel的容量较大时,可能会增加系统的内存开销,也可能会导致Channel中的元素被占用的时间较长,从而影响程序的响应性。
13. Channel的内存模型是什么
在Go语言中,Channel的内存模型是基于通信顺序进程(Communicating Sequential Processes,CSP)模型的。CSP模型是一种并发计算模型,它将并发程序看作是一组顺序进程,这些进程通过Channel进行通信和同步。
在CSP模型中,每个进程都是独立的,它们之间通过Channel进行通信。Channel是一个具有FIFO特性的数据结构,用于在多个进程之间传递数据。当一个进程向Channel发送数据时,它会阻塞等待,直到另一个进程从Channel中接收到数据。同样地,当一个进程从Channel中接收数据时,它也会阻塞等待,直到另一个进程向Channel发送数据。
在Go语言中,Channel的内存模型采用了CSP模型的概念,即每个Channel都是一个独立的顺序进程。当一个进程向Channel发送数据时,数据会被复制到Channel的缓冲区或者直接发送到接收方。当一个进程从Channel中接收数据时,数据会被从Channel的缓冲区中取出或者等待发送方发送数据。
14. 如何在Channel中传递复杂的数据类型
在Go语言中,Channel可以传递任何类型的数据,包括复杂的数据类型。如果要在Channel中传递复杂的数据类型,可以将其定义为一个结构体,然后通过Channel进行传递。
例如,假设我们有一个结构体类型Person,它包含姓名和年龄两个字段:
type Person struct {Name stringAge int
}
我们可以定义一个Channel,用于传递Person类型的数据:
ch := make(chan Person)
现在我们可以在不同的Goroutine中向Channel发送和接收Person类型的数据:
// 发送Person类型数据到Channel
go func() {p := Person{Name: "Alice", Age: 18}ch <- p
}()// 从Channel接收Person类型数据
p := <-ch
fmt.Println(p.Name, p.Age)
注意,如果要在Channel中传递复杂的数据类型,需要确保该类型是可导出的。
15. Channel和goroutine之间的关系是什么
在Go语言中,Channel和Goroutine是密切相关的,它们可以说是Go语言并发编程的两个重要组件。
Goroutine是Go语言中轻量级的线程实现,可以在一个进程中创建成千上万个Goroutine,并且它们的创建和销毁的代价非常小,因此非常适合在高并发的场景下使用。Goroutine的调度是由Go运行时系统(runtime)负责的,它采用协作式调度,可以自动地在多个线程之间切换,以达到高效利用CPU的目的。
Channel是Goroutine之间通信的一种方式,它可以用于在不同的Goroutine之间传递数据。Channel提供了两个基本操作:发送和接收。通过向Channel发送数据,一个Goroutine可以将数据传递给另一个Goroutine;通过从Channel接收数据,一个Goroutine可以获取其他Goroutine传递过来的数据。
因此,可以说Channel和Goroutine之间是一种协作关系:Goroutine可以通过Channel与其他Goroutine进行通信,以实现协作和共享数据,从而完成复杂的并发任务。同时,Channel的实现也依赖于Goroutine和Go运行时系统,它们共同构成了Go语言并发编程的基础。
16. 在Go语言中,Channel和锁的使用场景有哪些区别
在Go语言中,Channel和锁(sync.Mutex等)都可以用于并发编程中的同步和共享数据,但它们的使用场景有一些区别。
Channel通常用于Goroutine之间传递数据,并发的Goroutine之间可以通过Channel进行同步。使用Channel可以避免锁的问题,例如死锁、饥饿等问题。Channel可以将数据在多个Goroutine之间进行传递和共享,而且在数据传递的过程中,不需要使用锁来保证数据的安全性,这也是Channel比锁更加安全和高效的原因之一。因此,当需要在不同的Goroutine之间传递数据时,使用Channel是比较合适的选择。
锁通常用于对共享资源进行保护,防止多个Goroutine同时访问和修改同一个共享资源,从而导致数据的竞争和不一致。使用锁可以保证同一时刻只有一个Goroutine能够访问和修改共享资源,从而保证数据的安全性和一致性。当需要对共享资源进行保护时,使用锁是比较合适的选择。
Channel和锁都是Go语言中常用的并发编程工具,它们各自有不同的使用场景。在实际开发中,应根据具体的需求选择合适的并发编程工具来实现同步和共享数据。
四 最后
通过这场面试,感觉大厂比较考验发散性思维,为什么这样做,这样做有什么用,会得到什么好处,跟其他相比有什么优势,这确实是我之前所不具备的,思考问题一定要深入原理,多思考背后的问题,这样才能快速成长起来。
希望能够坚持到这里朋友们,以后再遇到Channel的问题,不会再被难住,加油!如果友友们觉得写的还可以,记得一键三连哦!
未来不是预测,而是创造。只要我们努力、积极地行动,未来就充满着无限的可能
相关文章:
这次一次性通关channel!
一 前言 前几天面试某大厂的云原生岗位,原本是一个轻松愉快的过程,当问到第二个问题,我就发现事情的不对劲,先复盘一下面试官有关Channel的问题,然后再逐一解决,最后进行扩展,这次一定要一次性…...
线程数控制
项目需求:javaMATLAB并行开发 在java中调用由MATLAB转成的jar包的代码,需要调用到底层的MATLAB服务。每次只能一个线程调用,当多个线程同时调用MATLAB时,MATLAB会报错。导致整个java服务挂掉。 现在增加线程控制,每…...

DC-6靶机
先去看看DC-6的官网描述,看看有没有给出提示信息 把这个线索信息先复制下来 cat /usr/share/wordlists/rockyou.txt | grep k01 > passwords.txt开始前先要吧 kali和DC-6靶机放在统一网段,都换成NAT模式 然后看一下DC-6的MAC地址 靶机的MAC地址00…...

SpringCloud入门Day01-服务注册与发现、服务通信、负载均衡与算法
SpringCloudNetflix入门 一、应用架构的演变 伴随互联网的发展,使用互联网的人群越来越多,软件应用的体量越来越大和复杂。而传统单体应用 可能不足以支撑大数据量以及发哦并发场景应用的框架也随之进行演变从最开始的单体应用架构到分布式(…...

java-IDEA MAVEN查看依赖树,解决jar包重复和冲突
如果这里面的依赖关系有红线,就说明有包冲突,一般都是版本不一致,可以在idea里下一个插件Maven Helper,点击install并重启IDEA 打开pom.xml文件,在下方会出现Dependency Analyzer,选择它会出现重复依赖列表,选择对应的依赖,右键红…...

参考RabbitMQ实现一个消息队列
文章目录 前言小小消息管家1.项目介绍2. 需求分析2.1 API2.2 消息应答2.3 网络通信协议设计 3. 开发环境4. 项目结构介绍4.1 配置信息 5. 项目演示 前言 消息队列的本质就是阻塞队列,它的最大用途就是用来实现生产者消费者模型,从而实现解耦合以及削峰填…...
SpringBoot+JWT
一、maven坐标 <!-- JWT依赖 --><dependency><groupId>io.jsonwebtoken</groupId><artifactId>jjwt</artifactId><version>0.9.1</version></dependency><dependency><groupId>com.auth0</groupId>&…...
Cad二次开发EqualPoint
在 CAD 软件的二次开发中,Tolerance.Global.EqualPoint 是一个特定的属性或方法,用于表示全局的相等性公差值。这个属性或方法通常是由 CAD 软件的开发平台或 API 提供的,用于处理浮点数的相等性比较。 具体来说,Tolerance.Globa…...

20230806将ASF格式的视频转换为MP4
20230806将ASF格式的视频转换为MP4 2023/8/6 18:47 缘起,自考中山大学的《计算机网络》,考试《数据库系统原理》的时候找到视频,由于个人的原因,使用字幕更加有学习效率! 由于【重型】的PR2023占用资源较多,…...
【MySQL】——常用接口API即相关函数说明
目录 1、MySQL结构体的说明 1、MYSQL结构体 2.MYSQL_RES结构体 3. MYSQL_FIELD 2. 接口的使用步骤 3、mysql_init()——MYSQL对象初始化 4、mysql_real_connect()——数据库引擎建立连接 5. mysql_query()——查询数据库某表内容 6、mysql_real_query——执行SQL语句 …...
ts + axios + useRequest (ahooks)—— 实现请求封装
现在越来越多的项目开始ts化,我们今天就一块学习一下,关于ts的请求封装。 首先要安装两个依赖: npm i axios -S npm i ahooks -S 引入: import { useRequest } from ahooks; import axios, { AxiosRequestConfig, AxiosRespo…...

Springboot @Validated注解详细说明
在Spring Boot中,Validated注解用于验证请求参数。它可以应用在Controller类或方法上 1、引入依赖 <dependency><groupId>org.springframework.boot</groupId><artifactId>spring-boot-starter-validation</artifactId> </depen…...
STM32初学者,到底选标准库还是HAL库?
当初学者尝试学习STM32开发时,通常会面临一个关键的选择:是选择STM32的标准库,还是HAL库?这两个库各自有着优势与适用场景,本文将从多个角度分析,帮助初学者更好地选择适合自己的库。 在开始之前ÿ…...

小学生作业随机加减乘除运算计算习题答案 html源码
小学生作业随机加减乘除运算计算习题答案 html源码 这道题目提供了多种选项,包括运算符和输入的运算数范围。题目数量也可以选择。如果你选择好了选项,就可以点击出题按钮进行练习。 为了方便,题目答案可以打印出来。但是,如果隐藏了横线,就会去除等号后面的下划线。推荐使用…...

nvm下载安装配置
一、作用 nvm是node版本管理的工具,具有管理、下载、切换node版本等能力。经常不同项目需要依赖不同版本的node,此时nvm就能有效的解决node版本切换的问题。 二、nvm下载安装配置 (1)安装包地址 https://github.com/coreybutl…...
2023-08-07力扣每日一题
链接: 344. 反转字符串 题意: 如题 解: 初级算法做过的题啊-感觉这几天重复题还蛮多的 实际代码: #include<iostream> #include<vector> #include<algorithm> using namespace std; /* void reverseStri…...

uni——不规则tab切换(skew)
案例展示 案例代码 <!-- 切换栏 --> <view class"tabBoxs"><view class"tabBox"><block v-for"(item,index) in tabList" :key"index"><view class"tabItem":class"current item.id&…...

Docker安装Grafana以及Grafana应用
Doker基础 安装 1、 卸载旧的版本 sudo yum remove docker docker-client docker-client-latest docker-common docker-latest docker-latest-logrotate docker-logrotate docker-engine 2、需要的安装包 sudo yum install -y yum-utils 3、设置镜像的仓库 yum-config-m…...

OpenSource - 分布式重试平台
文章目录 概述重试方案对比设计思想流量管理平台预览场景应用强通知场景发送MQ场景回调场景异步场景 概述 在当前广泛流行的分布式系统中,确保系统数据的一致性和正确性是一项重大挑战。为了解决分布式事务问题,涌现了许多理论和业务实践,其…...
oracle稳定执行计划
二、稳定执行计划 (一)sql profile的好处 稳定执行计划 在不能修改目标sql的sql文本的情况下使目标sql语句按照指定的执行计划运行。 1、automatic类型的sql profile 本质是针对目标sql的一些额外的调整信息,这些额外的调整信息需要与原目标s…...
多模态商品数据接口:融合图像、语音与文字的下一代商品详情体验
一、多模态商品数据接口的技术架构 (一)多模态数据融合引擎 跨模态语义对齐 通过Transformer架构实现图像、语音、文字的语义关联。例如,当用户上传一张“蓝色连衣裙”的图片时,接口可自动提取图像中的颜色(RGB值&…...

ServerTrust 并非唯一
NSURLAuthenticationMethodServerTrust 只是 authenticationMethod 的冰山一角 要理解 NSURLAuthenticationMethodServerTrust, 首先要明白它只是 authenticationMethod 的选项之一, 并非唯一 1 先厘清概念 点说明authenticationMethodURLAuthenticationChallenge.protectionS…...
C++中string流知识详解和示例
一、概览与类体系 C 提供三种基于内存字符串的流,定义在 <sstream> 中: std::istringstream:输入流,从已有字符串中读取并解析。std::ostringstream:输出流,向内部缓冲区写入内容,最终取…...

selenium学习实战【Python爬虫】
selenium学习实战【Python爬虫】 文章目录 selenium学习实战【Python爬虫】一、声明二、学习目标三、安装依赖3.1 安装selenium库3.2 安装浏览器驱动3.2.1 查看Edge版本3.2.2 驱动安装 四、代码讲解4.1 配置浏览器4.2 加载更多4.3 寻找内容4.4 完整代码 五、报告文件爬取5.1 提…...
【Java学习笔记】BigInteger 和 BigDecimal 类
BigInteger 和 BigDecimal 类 二者共有的常见方法 方法功能add加subtract减multiply乘divide除 注意点:传参类型必须是类对象 一、BigInteger 1. 作用:适合保存比较大的整型数 2. 使用说明 创建BigInteger对象 传入字符串 3. 代码示例 import j…...
【生成模型】视频生成论文调研
工作清单 上游应用方向:控制、速度、时长、高动态、多主体驱动 类型工作基础模型WAN / WAN-VACE / HunyuanVideo控制条件轨迹控制ATI~镜头控制ReCamMaster~多主体驱动Phantom~音频驱动Let Them Talk: Audio-Driven Multi-Person Conversational Video Generation速…...

论文笔记——相干体技术在裂缝预测中的应用研究
目录 相关地震知识补充地震数据的认识地震几何属性 相干体算法定义基本原理第一代相干体技术:基于互相关的相干体技术(Correlation)第二代相干体技术:基于相似的相干体技术(Semblance)基于多道相似的相干体…...

push [特殊字符] present
push 🆚 present 前言present和dismiss特点代码演示 push和pop特点代码演示 前言 在 iOS 开发中,push 和 present 是两种不同的视图控制器切换方式,它们有着显著的区别。 present和dismiss 特点 在当前控制器上方新建视图层级需要手动调用…...
站群服务器的应用场景都有哪些?
站群服务器主要是为了多个网站的托管和管理所设计的,可以通过集中管理和高效资源的分配,来支持多个独立的网站同时运行,让每一个网站都可以分配到独立的IP地址,避免出现IP关联的风险,用户还可以通过控制面板进行管理功…...

代码规范和架构【立芯理论一】(2025.06.08)
1、代码规范的目标 代码简洁精炼、美观,可持续性好高效率高复用,可移植性好高内聚,低耦合没有冗余规范性,代码有规可循,可以看出自己当时的思考过程特殊排版,特殊语法,特殊指令,必须…...