Go (一) 基础部分5 -- 单元测试,协程(goroutine),管道(channel)

一、单元测试

Go自带一个轻量级的"测试框架testing"和自带的"go test"命令来实现单元测试和性能测试。

1.确保每个函数时可运行,并且运行结果是正确的。
2.确保写出来的代码性能是好的。
3.单元测试能及时的发现程序设计或实现的逻辑错误,使问题及早暴露,便于问题的定位解决。而性能测试的重点在于发现程序设计上的一些问题,让程序能够在高并发的情况下还能保持稳定。

运用测试用例的指令:

go test:运行正确时,无日志,运行错误时,会输出日志

go test -v:运行正确或者错误都会输出日志

1.1、单元测试的快速入门(判断一个函数的执行结果是否符合预期)

1.测试用例文件必须以"_test.go"结尾,文件不能命名为:_test.go,test.go,test_xxx.go;

2.测试用例文件内任何 "Test开头且首字母大写"的函数(例:TestXxx)都会被执行,文件内不需要写main函数;

3.TestXxx(t *testing.T)的形参类型必须时*testing.T;

4.出现错误时,可以使用"t.Fatalf"来格式化错误信息,并退出程序;

5."t.Logf"方法可以输出相应的日志;

6.测试用例函数,没有main函数,也正常执行了,这也是测试用例的方便之处;

7.PASS表示测试用例运行成功,FAIL表示测试用例运行失败;

8.测试单个文件"cal_test.go",一定要带上被测试的原文件:go test -v cal_test.go main.go

9.测试单个方法:go test -v -test.run(固定参数) TestAddUpper(函数名)

package main

import (
	"fmt"
	"testing"
)

// 使用go test命令,能够自动执行如下形式的任何函数
// func TestXxx(*testing.T),其中Xxx可以是任何字母或字符串(第一个字母不能是[a-z])
func TestAddUpper(t *testing.T)  {
	res := AddUpper(10)
	if res != 55 {
		t.Fatalf("AddUpper函数执行错误,期望值:%v 返回值:%v",55,res)
	}
	t.Logf("AddUpper函数执行成功")
}

func TestSudada(t *testing.T)  {
	fmt.Println("函数TestSudada被执行")
}

测试函数:xxx.go,文件内包含要测试的"函数",文件内不需要写main函数。

package main

// xxx_test.go 内调用的函数
func AddUpper(n int) int  {
	res:=0
	for i:=0;i<=10;i++{
		res+=i
	}
	return res
}

"go test"命令执行错误时的返回结果

"go test"命令执行正确时的返回

1.2、单元测试-综合案例

测试用例文件:store_test.go(测试结构体的序列化和反序列化)

package main

import (
	"fmt"
	"testing"
)

// 测试用例TestStore
func TestStore(t *testing.T)  {
	// 先创建结构体变量
	var monster = Monster{
		Name: "牛魔王",
		Age: 18,
		Skill: "蛮牛冲撞",
	}
	StoreRes:=monster.Store()
	if !StoreRes {
		fmt.Println("Store函数测试错误,返回值不为ture")
	}
	// 返回值:
	// === RUN   TestStore
	// Store方法执行成功,test.txt文件保存成功
	// --- PASS: TestStore (0.00s)
}

// 测试用例TestReStore
func TestReStore(t *testing.T)  {
	// 先创建结构体变量
	var monster = Monster{}
	ReStoreRes:=monster.ReStore()
	if !ReStoreRes {
		fmt.Println("ReStore函数测试错误,返回值不为ture")
	}
	// 返回值:
	// === RUN   TestReStore
	// ReStore方法执行成功,反序列化的值为 &{牛魔王 18 蛮牛冲撞}
	// --- PASS: TestReStore (0.00s)
}

被测试对象:main.go(结构体和方法)

package main

import (
	"encoding/json"
	"fmt"
	"io/ioutil"
)

type Monster struct {
	Name string
	Age int
	Skill string
}


func (this *Monster)Store() bool {
	// 序列化结构体变量
	data, err := json.Marshal(this)
	if err != nil {
		fmt.Println("序列化失败",err)
		return false
	}
	// 把序列化后的数据,写入到test.txt文件内
	WriteFileErr :=ioutil.WriteFile("test.txt",data,0666)
	if WriteFileErr != nil{
		fmt.Println("文件保存失败",WriteFileErr)
		return false
	}
	fmt.Println("Store方法执行成功,test.txt文件保存成功")
	return true
}


func (this *Monster)ReStore() bool {
	// 从文件中,读取序列化的数据
	data, ReadFileErr := ioutil.ReadFile("test.txt")
	if ReadFileErr != nil{
		fmt.Println("文件读取失败",ReadFileErr)
		return false
	}
	// 将读到的数据,执行反序列化
	err := json.Unmarshal(data, this)
	if err != nil{
		fmt.Println("反序列化失败",err)
		return false
	}
	fmt.Println("ReStore方法执行成功,反序列化的值为:",this)
	return true
}

二、goroutine(协程)

一个go线程上,可以起多个协程,协程是轻量级的线程。

Go协程的特点有独立的栈空间,共享程序堆空间,调度由用户控制,协程是轻量级的线程

2.1、gorouting快速入门案例

协程的执行流程:
1.程序开始(进程/主线程开始执行);
2.go test() 开启协程:协程此时开始执;行,如果主线程退出了,那么无论协程是否执行完毕,都会退出;
3.主线程(main)执行代码逻辑;
4.主线程结束(程序退出)。

案例:在主线程中,启动一个gorouting,该协程每隔1秒输出一个"hello world"

package main

import (
	"fmt"
	"time"
)

// 
func test()  {
	for i:=0;i<10;i++ {
		fmt.Println("test() hello world",i)
		time.Sleep(time.Second)
	}
}

func main() {
	// 开启一个协程
	go test()
	for i:=0;i<10;i++ {
		fmt.Println("main() hello world",i)
		time.Sleep(time.Second)
	}
}

// 输出结果
main() hello world 0
test() hello world 0
test() hello world 1
main() hello world 1
main() hello world 2
test() hello world 2
test() hello world 3
main() hello world 3
main() hello world 4
test() hello world 4
test() hello world 5
main() hello world 5
main() hello world 6
test() hello world 6
test() hello world 7
main() hello world 7
main() hello world 8
test() hello world 8
test() hello world 9
main() hello world 9

2.1.1、协程的快速入门小结

1.主线程是物理线程,作用在CPU上。是重量级的,非常耗费cpu资源。

2.协程是从主线程开启的,是轻量级的线程,是逻辑态,对资源消耗相对小。

3.Golang的协程机制是重要的特点,可以轻松开启上万个协程。

2.2、MPG模式

M:操作系统的主线程

P:协程执行需要的上下文

G:协程(gorouting)

2.3、Go设置运行cpu数目

为了充分利用多CPU的优势,在golang程序中,设置运行的cpu数目(go1.8版本之后,默认让程序运行在多核上,可不设置)

package main

import (
	"fmt"
	"runtime"
)

func main() {
	// 常看当前主机的CPU核数
	cpuNum:=runtime.NumCPU()
	fmt.Println(cpuNum)

	// 可以自定义设置golang运行的cpu数
	runtime.GOMAXPROCS(5)
	fmt.Println("ok")
}

三、channel(管道)

在运行程序时,如何知道是否存在资源争抢的问题?

在编译程序时,增加一个参数 -race 即可。

3.1、全局锁的定义和使用

// 定义一个全局互斥锁
var lock sync.Mutex

func main()  {
	// 加锁
	lock.Lock()

    代码逻辑...

	// 解锁
	lock.Unlock()
}

3.2、channel(管道)的基本介绍 - 引用类型

3.2.1、为什么要使用channel

1.channel本质就是一个数据结构-队列 (先进先出)
2.线程安全,多gorouting访问时,不需要加锁,就是说channel本身就是线程安全的 (多个协程操作同一个管道时,不会发生资源竞争问题);
3.channel是有类型的,一个string类型的channel只能存放string类型数据;

3.2.2、channel的基本语法

var 变量名 chan 数据类型

var intChan chan int (intChan用于存放int数据)

var mapChan chan map[int]string (mapChan用于存放map[int]string数据)

var perChan chan Person (perChan用于存放结构体)

var perChan chan *Person (perChan用于存放结构体指针)

说明:

channel是引用类型

channel必须初始化才能雪茹数据,即make后才能使用

3.3、channel的快速入门(管道创建,写数据,读数据)

package main

import "fmt"

func main() {
	// 定义intChan
	var intChan chan int

	// 初始化intChan:初始化类型为chan,类型为int,容量为3
	intChan = make(chan int, 3)

	// 查看管道的值是什么
	fmt.Println(intChan)  // 得到的是一个指针地址

	// 向管道中写入数据(写入的数据数量,不能超过管道的容量,如果超过会报错deadlock)
	intChan<- 10  // "<- 10" 向管道内写入一个数据"10"
	num:=20
	intChan<-num  // "<- num" 向管道内写入一个变量的值"20"

	// 查看管道的长度(追加了2个数据)
	fmt.Println(len(intChan)) // 返回值:2
	// 查看管道的cap(容量)(make时设置的容量为3)
	fmt.Println(cap(intChan)) // 返回值:3

	// 从管道中取出一个值(长度会减少,容量不变)
	var num2 int
	num2 = <-intChan
	fmt.Println(num2) // 返回值为:10
	//var num3 int
	//num3 = <-intChan
	//fmt.Println(num3) // 返回值为:20

	// 取出一个数据,不接收
	<-intChan

	// 管道的数据全部取完后,会报错deadlock
	var num4 int
	num4 = <-intChan
	fmt.Println(num4) // 返回值为:deadlock!
}

3.3.1、channel的注意事项

1.channel定义好了数据类型之后,只能存放指定的数据类型;

2.channel的数据存满之后,就不能在存入了;

3.channel的数据取完之后,才可以继续存入;

4.在没有使用协程的情况下,如果channel的数据取完了,再取的话,就会报错deadlock。

3.3.2、案例1:创建一个intChan,存放int类型的数据,然后取出

package main

import "fmt"

func main() {
	// 定义intChan
	var intChan chan int
	// 初始化chan
	intChan = make(chan int, 10)
    // 给管道添加值
	intChan<-10
	intChan<-20
	intChan<-30
	// 从管道取值
	num1:=<-intChan
	num2:=<-intChan
	num3:=<-intChan
	fmt.Println(num1,num2,num3)
}

3.3.3、案例2:创建一个mapChan,存放map[string]string数据,然后取出

package main

import "fmt"

func main() {
	// 定义chan
	var mapChan chan map[string]string
	// 初始化chan
	mapChan = make(chan map[string]string,10)
	// 给管道添加值(map类型先make)
	m1 := make(map[string]string,10)
	m1["name1"]="北京"
	m1["name2"]="天津"
	m2 := make(map[string]string,10)
	m2["name1"]="上海"
	m2["name2"]="南京"
	mapChan<-m1
	mapChan<-m2

	// 从管道取值
	m11:=<-mapChan
	m22:=<-mapChan
	fmt.Println(m11)  // map[name1:北京 name2:天津]
	fmt.Println(m22)  // map[name1:上海 name2:南京]
}

3.3.4、案例3:创建一个catChan,存放Cat结构体变量数据,然后取出

package main

import "fmt"

type Cat struct {
	Name string
	Age int
}

func main() {
	// 定义chan
	var catChan chan Cat
	// 初始化chan
	catChan = make(chan Cat,10)
	cat1:=Cat{
		Name: "Tom",
		Age: 2,
	}
	cat2:=Cat{
		Name: "TTome",
		Age: 2,
	}
	// 给管道赋值
	catChan<-cat1
	catChan<-cat2

	// 管道取值
	cat11:=<-catChan
	cat22:=<-catChan
	fmt.Println(cat11)  // 返回值:{Tom 2}
	fmt.Println(cat22)  // 返回值:{TTome 2}
}

3.3.5、案例4:创建一个allChan,存放任意类型的数据,然后取出

package main

import "fmt"

type Cat struct {
	Name string
	Age int
}

func main() {
	// 定义chan
	allChan := make(chan any,3)
	// chan赋值
	allChan<-"sudada"
	allChan<-123
	cat:=Cat{
		Name: "Tom",
		Age: 2,
	}
	allChan<-cat
	// 只想获取管道的第三个值时(需要现将前2个值推出)
	<-allChan
	<-allChan
	// 获取到的第三个值
	cat1:=<-allChan
	fmt.Println(cat1)  // 返回值:{Tom 2}

	// 查看管道内值的类型
	fmt.Printf("%T\n",cat1)  // 返回值:main.Cat
	// fmt.Println(cat1.Name)  // 直接获取结构体的值会报错:cat1.Name undefined (type any has no field or method Name)

	// 使用类型断言
	newCat:=cat1.(Cat)
	fmt.Println(newCat.Name) // 返回值:Tom
}

3.4、channel的遍历和关闭

3.4.1、channel的关闭 close(xxxChan)

使用内置函数close()就可以关闭channel,当channel关闭后,就不能在往里面写数据,但是仍然可以读取数据

package main

import "fmt"

func main() {
	// 定义并初始化chan
	intChan:=make(chan int,3)
	// 管道赋值
	intChan<-100
	intChan<-200
	// 管道关闭
	close(intChan)
	//intChan<-300  // 管道关闭后,在往里面写数据时,会报错:panic: send on closed channel
	// 管道关闭后,读取值
	num1:=<-intChan
	num2:=<-intChan
	fmt.Println(num1)  // 返回值:100
	fmt.Println(num2)  // 返回值:200
}

3.4.2、channel的遍历(for -- range)

1.在遍历时,如果channel没有关闭,则会出现deadlock的错误

2.在遍历时,如果channel已经关闭,则会正常遍历数据,遍历完毕后退出。

package main

import "fmt"

func main() {
	// 定义并初始化chan
	intChan:=make(chan int,100)
	// 管道赋值
	for i:=1;i<=100;i++{
		intChan<-i
	}
	// 关闭管道(如果不关闭会报错deadlock)
	close(intChan)

	// for-range遍历管道
	for v:=  range intChan {
		fmt.Println(v)  // 返回值:1...100
	}
}

3.5、gorouting和chnnel结合使用的案例

1.开启一个writeData协程,向管道intChan写入10个整数;

2.开启一个readData协程,从管道intChan内读取10个整数;

3.writeData和readData操作的是同一个管道;

4.主线程需要等待writeData和readData协程都完成工作才退出;

流程图解:

代码实现:

package main

import (
	"fmt"
	"time"
)

func writeData(intChan chan int)  {
	for i:=0;i<10;i++{
		// 写之前等1秒,模拟一边写一边读的场景
		time.Sleep(time.Second)
		// 管道内放入数据
		intChan<-i
		fmt.Println("writeData: ",i)
	}
	// 关闭管道(不在写数据之后,就把管道关闭,但还可以继续读管道的数据)
	close(intChan)
}

func readData(intChan chan int, exitChan chan bool)  {
	for {
		// 读之前等1秒,模拟一边写一边读的场景
		time.Sleep(time.Second)
		// 管道内取值,并判断是否取值成功
		v, ok := <-intChan
		if !ok {
			break
		}
		fmt.Println("readData: ",v)
	}
	// intChan管道内的所有值全部都取出后,往exitChan写一个数据,并关闭管道
	exitChan<-true
	close(exitChan)
}

func main() {
	// 创建2个管道
	intChan := make(chan int,10)
	exitChan := make(chan bool,1)

	// 调用协程
	go writeData(intChan)
	go readData(intChan,exitChan)

	// 主进程读取exitChan管道内的数据,取到值之后再退出,否则就一直等待
	for {
		// 管道内取值,并判断是否取值成功
		_, ok := <-exitChan
		if ok {
			break
		}
	}
}

// 返回值:
//writeData:  0
//readData:  0
//writeData:  1
//readData:  1
//writeData:  2
//readData:  2
//writeData:  3
//readData:  3
//writeData:  4
//readData:  4
//writeData:  5
//readData:  5
//writeData:  6
//readData:  6
//writeData:  7
//readData:  7
//writeData:  8
//readData:  8
//writeData:  9
//readData:  9

3.6、阻塞

编译器在运行时,发现一个管道只有写,而没有读,就会阻塞。(如果有(缓慢的)读取管道内的数据时,就不会阻塞)

举例:管道的容量为10,但是放入管道的数据量超过了10,或者一直没有取出管道内的数据,就会阻塞,报错deadlock;

案例1:一直往管道内写数据,当写入的数据量,超过管道的容量时,就会阻塞,报错deadlock

package main

import (
	"fmt"
)

func writeData(intChan chan int)  {
	for i:=0;i<10;i++{
		// 写之前等1秒,模拟一边写一边读的场景
		//time.Sleep(time.Second)
		// 管道内放入数据
		intChan<-i
		fmt.Println("writeData: ",i)
	}
	// 关闭管道(不在写数据之后,就把管道关闭,但还可以继续读管道的数据)
	close(intChan)
}

func readData(intChan chan int, exitChan chan bool)  {
	for {
		// 读之前等1秒,模拟一边写一边读的场景
		//time.Sleep(time.Second)
		// 管道内取值,并判断是否取值成功
		v, ok := <-intChan
		if !ok {
			break
		}
		fmt.Println("readData: ",v)
	}
	// intChan管道内的所有值全部都取出后,往exitChan写一个数据,并关闭管道
	exitChan<-true
	close(exitChan)
}

func main() {
	// 创建2个管道
	intChan := make(chan int,5)
	exitChan := make(chan bool,1)

	// 调用协程
	go writeData(intChan)
	//go readData(intChan,exitChan)

	// 主进程读取exitChan管道内的数据,取到值之后再退出,否则就一直等待
	for {
		// 管道内取值,并判断是否取值成功
		_, ok := <-exitChan
		if ok {
			break
		}
	}
}

// 返回值: fatal error: all goroutines are asleep - deadlock!

案例2:在往管道内写数据时,如果如果有协程在(缓慢的)去读/取(消费)管道内的数据,那么就不会阻塞

package main

import (
	"fmt"
	"time"
)

func writeData(intChan chan int)  {
	for i:=0;i<10;i++{
		// 写之前等1秒,模拟一边写一边读的场景
		//time.Sleep(time.Second)
		// 管道内放入数据
		intChan<-i
		fmt.Println("writeData: ",i)
	}
	// 关闭管道(不在写数据之后,就把管道关闭,但还可以继续读管道的数据)
	close(intChan)
}

func readData(intChan chan int, exitChan chan bool)  {
	for {
		// 读之前等1秒,模拟一边写一边读的场景
		time.Sleep(time.Second)
		// 管道内取值,并判断是否取值成功
		v, ok := <-intChan
		if !ok {
			break
		}
		fmt.Println("readData: ",v)
	}
	// intChan管道内的所有值全部都取出后,往exitChan写一个数据,并关闭管道
	exitChan<-true
	close(exitChan)
}

func main() {
	// 创建2个管道
	intChan := make(chan int,5)
	exitChan := make(chan bool,1)

	// 调用协程
	go writeData(intChan)
	go readData(intChan,exitChan)

	// 主进程读取exitChan管道内的数据,取到值之后再退出,否则就一直等待
	for {
		// 管道内取值,并判断是否取值成功
		_, ok := <-exitChan
		if ok {
			break
		}
	}
}

// 返回值:
writeData:  0
writeData:  1
writeData:  2
writeData:  3
writeData:  4
readData:  0
writeData:  5
readData:  1
writeData:  6
writeData:  7
readData:  2
readData:  3
writeData:  8
readData:  4
writeData:  9
readData:  5
readData:  6
readData:  7
readData:  8
readData:  9

3.7、协程求素数的实现

统计1-1000个数字中,哪些是素数?

package main

import (
	"fmt"
)

// 往管道内放入1000个数
func putNum(intChan chan int) {
	for i := 0; i < 100; i++ {
		intChan <- i
	}
	// 关闭intChan
	close(intChan)
}

// 从管道intChan内取数据,判断是否为素数,把素数放入primeChan。取完后往exitChan写入一个true
func putPrimeNum(intChan chan int, primeChan chan int, exitChan chan bool) {
	var flag bool
	for {
		// 从管道intChan内取数据
		num,ok:=<-intChan
		// 管道intChan内的数据取完就退出
		if !ok {
			break
		}
		// 假设是素数
		flag = true
		// 判断是否为素数
		for i:=2;i<num;i++{
			if num %i == 0 {  // 为0说明不是素数
				flag = false
				break
			}
		}
		if flag {
			// 把素数放入管道primeChan
			primeChan<-num
		}
	}
	// 向退出的管道exitChan写入一个true即可
	fmt.Println("协程putPrimeNum取不到数据,退出")
	exitChan<-true
}

func main() {
	// 存放整数的chan
	var intChan chan int
	intChan = make(chan int, 100)
	// 存放素数的chan
	var primeChan chan int
	primeChan = make(chan int, 200)
	// 标识退出的chan
	var exitChan chan bool
	exitChan = make(chan bool, 4)

	// 开启协程,向intChan放入1000个数
	go putNum(intChan)
	// 开启协程,从intChan里面取数据,并判断是否为素数(如果是就放入primeChan)
	for i:=0;i<4;i++{
		go putPrimeNum(intChan,primeChan,exitChan)
	}

	// 退出主线程之前,先判断exitChan管道内的值是否都会true
	go func(){
		for i:=0;i<4;i++{
			<-exitChan
		}
		// 如果从exitChan管道内取出了4个true,就可以退出主线程了
		close(primeChan)
	}()

	// 遍历primeChan,把值取出
	for{
		num,ok:=<-primeChan
		if !ok {
			break
		}
		fmt.Println("素数: ",num)
	}
	fmt.Println("主线程退出")
	// 返回值:
	// 素数:  。。。
	// 协程putPrimeNum取不到数据,退出
	// 协程putPrimeNum取不到数据,退出
	// 协程putPrimeNum取不到数据,退出
	// 协程putPrimeNum取不到数据,退出
	// 主线程退出
}

3.8、channel的使用细节和注意事项

1.channel可以定义为只读或者只写模式(应用场景:把一个可读可写的管道传入一个函数,函数内对管道做下封装,只允许读/写)

只读模式,只写模式的代码定义:

package main

import "fmt"

func main() {
	// 定义一个"只写"的管道
	var intChanWrite chan<- int
	intChanWrite = make(chan<- int,3)
	intChanWrite<-10
	// "只写"的管道的不能取数据
	// num:=<-intChan

	// 定义一个"只读"的管道
	var intChanRead <-chan int
	intChanRead = make(<-chan int,3)
	num:=<-intChanRead
	fmt.Println(num)  // 因为管道内没有数据,这里取不到值
	// "只读"的管道的不能写数据
	//intChanRead<-10
}

2.使用select可以解决从管道内取数据阻塞的问题

package main

import (
	"fmt"
)

func main() {
	// 定义管道并存放值
	intChan := make(chan int,10)
	for i:=0;i<10;i++{
		intChan<-i
	}
	stringChan := make(chan string,5)
	for i:=0;i<5;i++{
		stringChan<-"hello"+fmt.Sprintf("%d",i)
	}

	// 传统的方式在遍历管道时,如果不关闭管道会deadlock(阻塞)
	// 实际开发中,不好确认什么时候关闭管道,此时使用select(解决从管道内取数据时,阻塞的问题)
	for  {
		select {
		// 如果管道intChan没有关闭,同时又取不到值时,不会deadlock(阻塞)会去下一个case取值
		case v:= <-intChan:
			fmt.Println("从intChan管道内取到的值",v)
		// 如果管道stringChan没有关闭,同时又取不到值时,不会deadlock(阻塞)会去下一个case取值
		case v:= <-stringChan:
			fmt.Println("从stringChan管道内取到的值",v)
		// 默认逻辑
		default:
			fmt.Println("管道内取不到值了。。。")
			return
		}
	}
}

3.协程中使用recover,解决协程中出现panic,导致程序崩溃的问题

package main

import (
	"fmt"
	"time"
)

func test()  {
	// defer + recover 捕获当前函数抛出的panic错误
	defer func() {
		err:=recover()
		if err != nil {
			fmt.Println("test函数发生错误",err)
		}
	}()
	// 函数的代码逻辑(这里故意写的错误代码,触发报错)
	var testmap map[int]string
	testmap[0]="sudada"
}

func main() {
	go test()
    // 正常的代码执行(不加recover时,协程报错,整个程序就退出了。加上recover后,协程报错了不影响正常代码的执行)
	for  {
		fmt.Println("main")
		time.Sleep(time.Second)
	}
	// 返回值:
	// main
	// test函数发生错误 assignment to entry in nil map
	// main
}

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

如若内容造成侵权/违法违规/事实不符,请联系我们进行投诉反馈qq邮箱809451989@qq.com,一经查实,立即删除!

相关文章

mysql基础-数据操作之增删改

目录 1.新增数据 1.1单条数据新增 1.2多条数据新增 1.3查询数据新增 2.更新 2.1单值更新 2.2多值更新 2.3批量更新 2.3.1 批量-单条件更新 2.3.2批量-多条件更新 2.4 插入或更新 2.5 联表更新 3.删除 本次分享一下数据库的DML操作语言。 操作表的数据结构&#xf…

Spark回归分析与特征工程

回归分析是统计学和机器学习中的一个重要分支&#xff0c;用于建立因变量与自变量之间的关系模型。在大数据领域&#xff0c;Apache Spark为回归分析提供了强大的工具和库&#xff0c;以处理大规模数据集。本文将深入探讨如何使用Spark进行回归分析以及如何进行特征工程&#x…

使用qtquick调用python程序,pytorch

一. 内容简介 使用qtquick调用python程序 二. 软件环境 2.1vsCode 2.2Anaconda version: conda 22.9.0 2.3pytorch 安装pytorch(http://t.csdnimg.cn/GVP23) 2.4QT 5.14.1 新版QT6.4,&#xff0c;6.5在线安装经常失败&#xff0c;而5.9版本又无法编译64位程序&#xf…

第1章 初识JavaScript

学习目标 了解JavaScript基本概念&#xff0c;能够说出JavaScript的作用、由来、组成和特点 熟悉常见浏览器的特点&#xff0c;能够说出浏览器的组成以及作用 掌握下载和安装Visual Studio Code编辑器&#xff0c;能够独立完成编辑器的下载和安装 掌握JavaScript代码引入方式…

Windows电脑无法睡眠解决办法

原因 电脑无法休眠的原因&#xff0c;是打开离开模式策略后&#xff0c;windows内核会持续调用CPU资源&#xff0c;导致系统一直在运行而无法关闭。关闭后就好了。 解决步骤 修改注册表 操作步骤如下: 按winR&#xff0c;输入regedit&#xff0c;打开注册表编辑页面。输入如下…

第11章 GUI Page462~476 步骤二十三,二十四,二十五 Undo/Redo ③实现“Undo/Redo”菜单项

工程六 添加“编辑”菜单和子菜单 菜单ID分别为 idMenuEditUndo 和 idMenuEditRedo 热键&#xff08;快捷键&#xff09;分别为CtrlZ 和 CtrlShiftZ 变量名分别为 MenuItemEditUndo 和 MenuItemEditRedo 分别添加事件 ActionLink类增加成员函数 运行效果&#xff1a;“添加…

Docker安装WebRTC下TURN服务

详细实现方式以及代码下载请前往 https://www.passerma.com/article/90 实现效果 一、手动构建镜像 1.新建Dockerfile文件 文件用于编译镜像 以alpine为基础镜像 添加coturn需要的依赖库 获取coturn并进行编译 通过start.sh启动turnserver服务 Dockerfile FROM alpineRUN ap…

AI与5G、IDC等成为数字经济的重要基础设施

AI与5G、IDC等已经成为数字经济的重要基础设施&#xff0c;它们的影响和作用不容忽视。随着技术的迅速发展&#xff0c;AI在各行各业都得到了广泛应用&#xff0c;并成为数字经济的核心驱动力之一。 首先&#xff0c;AI的兴起为数字经济带来了巨大的机遇。AI技术可以帮助企业从…

Vue2商品规格选择

Vue2Element-ui Vu2仿写拼多多商家后台规则选择&#xff0c;为什么用Vue2呢&#xff0c;因为公司用的Vue2... 样式不是很好看&#xff0c;自己调一下就行。 <template><div ref"inputContainer"><div>{{ combinationsResult }}</div><…

案例099:基于微信小程序的外卖小程序的研究与开发

文末获取源码 开发语言&#xff1a;Java 框架&#xff1a;SSM JDK版本&#xff1a;JDK1.8 数据库&#xff1a;mysql 5.7 开发软件&#xff1a;eclipse/myeclipse/idea Maven包&#xff1a;Maven3.5.4 小程序框架&#xff1a;uniapp 小程序开发软件&#xff1a;HBuilder X 小程序…

在Gitee上维护Erpnext源

在Gitee上维护Erpnext源 官方的frappe和erpnext地址: GitHub - frappe/frappe: Low code web framework for real world applications, in Python and Javascript GitHub - frappe/erpnext: Free and Open Source Enterprise Resource Planning (ERP) 1, 仓库地址输入frappe的官…

R 批量对多个变量进行单因素方差分析 批量计算均值±标准差

多个变量批量进行单因素方差 R实现 文章目录 一、批量生成均值标准差 P值二、添加协变量单因素方差分析&#xff0c;生成校正P值三、在分层情况下进行单因素方差分析四、添加协变量和交互项的单因素方差分析&#xff0c;生成交互项的P值 一、批量生成均值标准差 P值 数据结构如…

pyfolio工具结合backtrader分析量化策略组合,附源码+问题分析

pyfolio可以分析backtrader的策略&#xff0c;并生成一系列好看的图表&#xff0c;但是由于pyfolio直接install的稳定版有缺陷&#xff0c;开发版也存在诸多问题&#xff0c;使用的依赖版本都偏低&#xff0c;试用了一下之后还是更推荐quantstats。 1、安装依赖 pip install …

【STM32】STM32学习笔记-TIM输出比较(15)

00. 目录 文章目录 00. 目录01. 输出比较简介02. PWM简介03. 输出比较通道(高级)04. 输出比较通道(通用)05. 输出比较模式06. PWM基本结构07. PWM参数计算08. 舵机简介09. 舵机硬件电路10. 直流电机及驱动简介11. 直流电机硬件电路12. 附录 01. 输出比较简介 OC&#xff08;Ou…

ASP.NET可视化流程设计器源码

源码介绍: ASP.NET可视化流程设计器源码已应用于众多大型企事业单位。拥有全浏览器兼容的可视化流程设计器、表单设计器、基于角色的权限管理等系统开发必须功能&#xff0c;大大为您节省开发时间&#xff0c;是您开发OA.CRM、HR等企事业各种应用管理系统和工作流系统的最佳基…

Redis高级特性和应用(慢查询、Pipeline、事务、Lua)

Redis的慢查询 许多存储系统(例如 MySQL)提供慢查询日志帮助开发和运维人员定位系统存在的慢操作。所谓慢查询日志就是系统在命令执行前后计算每条命令的执行时间,当超过预设阀值,就将这条命令的相关信息(例如:发生时间,耗时,命令的详细信息)记录下来,Redis也提供了类似…

【开源】基于JAVA语言的服装店库存管理系统

目录 一、摘要1.1 项目介绍1.2 项目录屏 二、功能模块2.1 数据中心模块2.2 角色管理模块2.3 服装档案模块2.4 服装入库模块2.5 服装出库模块 三、系统设计3.1 用例设计3.2 数据库设计3.2.1 角色表3.2.2 服装档案表3.2.3 服装入库表3.2.4 服装出库表 四、系统展示五、核心代码5.…

梦想家内容管理系统(Dreamer CMS)跨站请求伪造漏洞

梦想家内容管理系统&#xff08;Dreamer CMS&#xff09;跨站请求伪造漏洞 目标:GitHub - iteachyou-wjn/dreamer_cms: Dreamer CMS 梦想家内容发布系统采用流行的SpringBoot搭建&#xff0c;支持静态化、标签化建站。不需要专业的后台开发技能&#xff0c;会HTML就能建站&…

java数据结构与算法刷题-----LeetCode63. 不同路径 II

java数据结构与算法刷题目录&#xff08;剑指Offer、LeetCode、ACM&#xff09;-----主目录-----持续更新(进不去说明我没写完)&#xff1a;https://blog.csdn.net/grd_java/article/details/123063846 很多人觉得动态规划很难&#xff0c;但它就是固定套路而已。其实动态规划只…

uniapp微信小程序投票系统实战 (SpringBoot2+vue3.2+element plus ) -后端鉴权拦截器实现

锋哥原创的uniapp微信小程序投票系统实战&#xff1a; uniapp微信小程序投票系统实战课程 (SpringBoot2vue3.2element plus ) ( 火爆连载更新中... )_哔哩哔哩_bilibiliuniapp微信小程序投票系统实战课程 (SpringBoot2vue3.2element plus ) ( 火爆连载更新中... )共计21条视频…