linux基础命令和示例

news2024/11/26 23:32:19

redis在go语言中的使用

以下说明以读者有redis基础的前提下进行

未学习redis的可以到b站1小时浅学redis了解大概,学会如何使用

【GeekHour】一小时Redis教程_哔哩哔哩_bilibili

以下开发环境以windows为测试环境,旨在练习redis在go语言中的使用

redis使用的是5.0.14的windows版本(正常开发项目redis都是在linux上使用,windows的redis版本很低),但基础功能都有,满足学习要求;

下载链接:https://github.com/tporadowski/redis/releases
(官网是没有windows版本的)

下载完成后,打开黑窗口,输入redis-cli显示

image-20240217171150476

即为成功,可以选择性下载redis的gui工具redisInsight,官网链接:

RedisInsight | The Best Redis GUI

使用时截图:

image-20240217171350265

左侧会显示key,点击后右侧会显示value的值,使用和查找起来较为方便,能够增加学习效率,当然还有很多功能,这里就不赘述

1引入go-redis库

为什么使用这个库,而不是其他的库呢?

理由:

  1. 性能高效: Go-Redis实现了高性能的Redis客户端,具有低延迟和高吞吐量。它采用了异步的方式处理多个并发请求,从而提高了性能。
  2. 完整支持Redis功能: Go-Redis库提供了对Redis的全面支持,包括对基本数据结构(字符串、哈希、列表、集合等)的操作,事务,流水线(pipeline)等功能。
  3. 易用性: Go-Redis提供了简单而直观的API,易于使用。它封装了与Redis的底层通信细节,使得开发者可以专注于业务逻辑而不必过多关注底层实现。
  4. 连接池: Go-Redis包含了连接池的支持,这有助于有效地管理和复用与Redis的连接,减少了连接的创建和销毁开销,提高了性能。
  5. Active development和社区支持: Go-Redis是一个活跃的项目,有一个庞大的社区支持。这意味着它经常得到更新和改进,并且有丰富的文档和社区资源可供参考。
  6. 灵活性: Go-Redis允许开发者选择不同的执行模式,如同步、异步和流水线,以满足不同场景下的需求。

总之,这个库用的人多,并且功能齐全,很适合在go语言中与redis交互;(好用就完事了!!!)

2在go项目中的初始化

其实很简单,和mysql初始化优点相似;

为了方便学习,初始化都写在一个文件中

如下:

package main

import (
	"fmt"
	"github.com/go-redis/redis/v8"
	"context"
)

func main() {
	// 创建一个新的Redis客户端实例
	client := redis.NewClient(&redis.Options{
		Addr:     "localhost:6379", // Redis服务器地址
		Password: "",               // 可选:如果有密码的话
		DB:       0,                // 使用的数据库编号
	})

	// 使用Ping测试连接
	pong, err := client.Ping(context.Background()).Result()
	if err != nil {
		fmt.Println("连接Redis出错:", err)
		return
	}
	fmt.Println("连接成功,Ping结果:", pong)

	// 在程序结束时关闭连接
	defer client.Close()
	
	// 在这里可以继续执行其他与Redis相关的操作
}
测试运行成功会输出:
连接成功,Ping结果: PONG 


我们知道redis共有五大基本数据类型:string,hash,list,set,Sorted Set

  1. 字符串(String): 字符串是最基本的数据类型,可以包含任何数据,比如文本、数字等。在Redis中,字符串可以用于存储各种类型的数据,例如配置信息、计数器、缓存等。常用的字符串操作命令有 SETGETINCRDECR 等。
  2. 哈希(Hash): 哈希是一个键值对集合,每个键值对称为字段和值。哈希适用于存储对象,可以将一个对象存储在一个哈希中,然后通过字段来访问对象的各个属性。常用的哈希操作命令有 HSETHGETHMSETHGETALL 等。
  3. 列表(List): 列表是一个有序的字符串元素集合,可以在列表的两端执行添加和删除操作。列表适用于实现队列、栈等数据结构,也可以用于存储一系列有序的数据。常用的列表操作命令有 LPUSHRPUSHLPOPRPOP 等。
  4. 集合(Set): 集合是一组唯一的无序字符串元素的集合。集合适用于存储一组不重复的元素,可以执行交集、并集、差集等操作。常用的集合操作命令有 SADDSREMSISMEMBERSMEMBERS 等。
  5. 有序集合(Sorted Set): 有序集合是集合的一个扩展,其中每个成员都关联了一个分数,可以按照分数对成员进行排序。有序集合适用于实现排行榜、时间线等功能。常用的有序集合操作命令有 ZADDZRANGEZSCOREZREMRANGEBYRANK 等。

在go-redis中也是如此,再加上发布订阅和事务处理基本就是要学习的go-redis知识了;

基础键值对相关操作 string

在go中简单设置键值对并查找键值对

简单设置一个键值对,来试试是否能够查找成功:

在go中设置key方法:Set
// 生成测试用的key
testKey := "test_key"
// 设置key的值,演示Set方法

err = client.Set(context.Background(), testKey, "Hello, Redis!", 0).Err()
if err != nil {
    fmt.Println("设置key出错:", err)
    return
}
fmt.Println("设置key成功")
//解释:
//通过创建testKey变量,你定义了一个测试用的键。

//使用client.Set方法设置了键test_key的值为字符串"Hello, Redis!",并指定了过期时间为0,表示不设置过期时间。

//通过检查错误(err != nil)来确保设置操作是否成功。如果设置失败,你输出了错误信息;否则,你输出了"设置key成功"。
检测key对应的value的值的方法:Get
// 获取key的值,演示Get方法
	value, err := client.Get(context.Background(), testKey).Result()
//result作用是:等待并返回实际的 Redis 命令执行结果。  这种异步操作和等待结果的方式有助于确保代码在获取 Redis 操作结果时不会阻塞整个程序,以便更好地处理并发性能。
	if err != nil {
		fmt.Println("获取key出错:", err)
		return
	}
	fmt.Printf("获取key的值:%s\n", value)
}
//成功运行时会返回:
//获取key的值:Hello, Redis!

redisInsight截图:
image-20240217172811496

获取指定键的当前值并设置一个新的值:GetSet
// 使用 GETSET 获取testKey原有的值存入到oldValue中,并设置新值为NewValue
	oldValue, err := client.GetSet(context.Background(), testKey, "NewValue").Result()
	if err != nil {
		fmt.Println("GETSET 操作出错:", err)
		return
	}

	fmt.Printf("旧值:%s\n", oldValue)

	// 获取更新后testKey中的值并存到newValue中
	newValue, err := client.Get(context.Background(), testKey).Result()
	if err != nil {
		fmt.Println("获取key出错:", err)
		return
	}
	fmt.Printf("新值:%s\n", newValue)

其实还是很简单的,都是一些重复代码段;

批量设置key的值:MSet
// 准备要设置的键值对
keyValues := map[string]interface{}{
    "key1": "value1",
    "key2": "value2",
    "key3": "value3",
}

// 使用 MSet 方法批量设置键值对
err = client.MSet(context.Background(), keyValues).Err()
if err != nil {
    fmt.Println("批量设置键值对出错:", err)
    return
}
//这个M的意思其实是mutiple(多个),不止针对普通string,对于HGet等等都能变成HMGet,批量增加

成功image-20240217180202018

增加整数的值的两个方法:Incr,Incrby

前者可以将值每次增加1,当要增加的值不存在时,会先自动置为0进行操作。下例就是

	// 示例使用的键名
	key := "myKey"

	// 使用 INCR 递增键的值, myKey初值为空,自动置为0+1了
	newValue, err := client.Incr(context.Background(), key).Result()
	if err != nil {
		fmt.Println("INCR 操作出错:", err)
		return
	}
	fmt.Printf("INCR 后 %s 的值为 %d\n", key, newValue)

	// 使用 INCRBY 指定增量递增键的值    1+5=6;
	increment := 5
	newValue, err = client.IncrBy(context.Background(), key, int64(increment)).Result()
	if err != nil {
		fmt.Println("INCRBY 操作出错:", err)
		return
	}
	fmt.Printf("INCRBY %d 后 %s 的值为 %d\n", increment, key, newValue)

image-20240218081020253

在实际开发中还是要看需要什么用什么

设置过期时间

之前的Set方法中,最后一个参数是0,表示的就是过期时间设置,默认以秒为单位,当为0时,表示永不过期,写10,就是10s后过期,写其他单位的数字就需要加单位,如1*time.Minute就是1分钟;

而正规一点的过期时间设置方法是:

使用Expire方法:直接设置key在生成后多少时间过期,比如10秒后过期

// 设置 key 的值
	err := client.Set(context.Background(), "myKey", "myValue", 0).Err()
	if err != nil {
		fmt.Println("设置 key 的值出错:", err)
		return
	}
	// 给 key 设置过期时间为 10 秒
	err = client.Expire(context.Background(), "myKey", 10*time.Second).Err()
	if err != nil {
		fmt.Println("给 key 设置过期时间出错:", err)
		return
	}

使用ExpireAt方法:表示过期的具体时间点,比如直接写成25年的1月1号0点0分过期;

// 设置 key 的值
	err := client.Set(context.Background(), "myKey", "myValue", 0).Err()
	if err != nil {
		fmt.Println("设置 key 的值出错:", err)
		return
	}

	// 给 key 设置过期时间为 10 秒后的某个特定时间
	expirationTime := time.Now().Add(10 * time.Second)
	err = client.ExpireAt(context.Background(), "myKey", expirationTime).Err()
	if err != nil {
		fmt.Println("给 key 设置过期时间出错:", err)
		return
	}

哈希键值对操作:

HGet

先举个例子方便理解,比如,原来的Get命令只能生成简单的键值对,但使用HGet,可以将多个键值对变成同一个哈希表对应的键值对,例如:

image-20240217210237677

这样就清楚了吧,代码示例:

创建和获取哈希类型
// 准备哈希名、字段和值
	hashName := "user:1"
	fieldName := "username"
	fieldValue := "JohnDoe"

	// 使用 HSet 方法设置哈希字段的值
	err := client.HSet(context.Background(), hashName, fieldName, fieldValue).Err()
	if err != nil {
		fmt.Println("设置哈希字段的值出错:", err)
		return
	}

	// 使用 HGet 方法获取哈希字段的值
	result, err := client.HGet(context.Background(), hashName, fieldName).Result()
	if err != nil {
		fmt.Println("获取哈希字段的值出错:", err)
		return
	}

批量创建哈希类型中的键值对

一看就会

	// 准备哈希名
	hashName := "user:1"

	// 使用 HMSet 一次性设置多个键值对
	keyValueMap := map[string]interface{}{
		"username": "JohnDoe",
		"email":    "john@example.com",
		"age":      "30",
	}

	err := client.HMSet(context.Background(), hashName, keyValueMap).Err()
	if err != nil {
		fmt.Println("一次性设置多个键值对出错:", err)
		return
	}

	fmt.Printf("成功一次性设置多个键值对到哈希表 %s\n", hashName)
}

到这里其实我们已经发现了,对于redis的操作基本都是相同的,只是调用函数和一两个参数的不同,最主要的还是学好redis基础,

哈希中使用Incr,Incrby增加值的大小

和string的很像,只是多了一个哈希的参数

// 哈希键名
	hashKey := "myHash"

	// 哈希字段名
	fieldName := "myField"

	// 使用Incr方法自增哈希中的字段值
	result, err := client.HIncrBy(hashKey, fieldName, 1).Result()
	if err != nil {
		fmt.Println("Error incrementing field:", err)
		return
	}

	fmt.Printf("Incr Result: %d\n", result)

	// 使用IncrBy方法自定义增量自增哈希中的字段值
	customIncrement := 5
	result, err = client.HIncrBy(hashKey, fieldName, int64(customIncrement)).Result()
	if err != nil {
		fmt.Println("Error incrementing field:", err)
		return
	}

	fmt.Printf("IncrBy Result: %d\n", result)

redis中的方法很多,之后就不一一展示了,只会挑选一些比较重要或者难以掌握的强调,毕竟有些简单操作真正使用时直接套代码就可以了;

Hkeys:获取指定哈希键名的哈希表中所有字段
// 哈希键名
	hashKey := "myHash"

	// 使用HKeys方法获取哈希表中所有字段
	fields, err := client.HKeys(hashKey).Result()
	if err != nil {
		fmt.Println("Error getting hash keys:", err)
		return
	}

HGetAll:返回哈希键中的所有字段及其对应的值
// 哈希键名
	hashKey := "myHash"

	// 使用HGetAll方法获取哈希表中所有字段及其对应的值
	fieldValues, err := client.HGetAll(hashKey).Result()
	if err != nil {
		fmt.Println("Error getting hash field values:", err)
		return
	}

	fmt.Printf("Hash field values for %s: %v\n", hashKey, fieldValues)

HMGet同时设置多个哈希字段
// 哈希键名
	hashKey := "myHash"

	// 使用HMSet方法设置多个哈希字段及其对应的值
	fieldsAndValues := map[string]interface{}{
		"field1": "value1",
		"field2": "value2",
		"field3": 123,
	}

	result, err := client.HMSet(hashKey, fieldsAndValues).Result()
	if err != nil {
		fmt.Println("Error setting hash fields:", err)
		return
	}

	fmt.Printf("HMSet Result: %v\n", result)
HSetNX:用于在哈希表中设置字段的命令,但是只有在字段不存在时才会设置成功
	// 哈希键名
	hashKey := "myHash"
	field := "myField"
	value := "myValue"

	// 使用HSetNX方法设置哈希字段,只有在字段不存在时才会设置成功
	result, err := client.HSetNX(hashKey, field, value).Result()
	if err != nil {
		fmt.Println("Error setting hash field:", err)
		return
	}

	if result {
		fmt.Printf("HSetNX: Field %s set to %s successfully.\n", field, value)
	} else {
		fmt.Printf("HSetNX: Field %s already exists in hash %s.\n", field, hashKey)
	}
}
HDel:删除hash中的键值对,支持批量删除

例:

image-20240218091433710

HExists:检测哈希中指定键是否存在

image-20240218091718568

该方法返回布尔值,存在就返回true,不存在返回false

3. List

1. LPush

从列表左边插入数据

// 插入一个数据
rdb.LPush(ctx,"key", "data1")

// LPush支持一次插入任意个数据
err := rdb.LPush(ctx,"key", 1,2,3,4,5).Err()
if err != nil {
	panic(err)
}

2. LPushX

跟LPush的区别是,仅当列表存在的时候才插入数据,用法完全一样。

err := rdb.LPushX(ctx, "key", "sss").Err()
	if err != nil {
		panic(err)
	}

3. RPop

从列表的右边删除第一个数据,并返回删除的数据

val, err := rdb.RPop(ctx,"key").Result()
if err != nil {
	panic(err)
}

fmt.Println(val)

4. RPush

从列表右边插入数据

// 插入一个数据
rdb.RPush(ctx,"key", "data1")

// 支持一次插入任意个数据
err := rdb.RPush(ctx,"key", 1,2,3,4,5).Err()
if err != nil {
	panic(err)
}

5. RPushX

跟RPush的区别是,仅当列表存在的时候才插入数据, 他们用法一样

err := rdb.RPushX(ctx,"key", "right_x").Err()
if err != nil {
	panic(err)
}

6. LPop

从列表左边删除第一个数据,并返回删除的数据

val, err := rdb.LPop(ctx,"key").Result()
if err != nil {
	panic(err)
}

fmt.Println(val)

7. LLen

返回列表的大小

val, err := rdb.LLen(ctx,"key").Result()
if err != nil {
	panic(err)
}

fmt.Println(val)

8. LRange

返回列表的一个范围内的数据,也可以返回全部数据

// 返回从0开始到-1位置之间的数据,意思就是返回全部数据
vals, err := rdb.LRange(ctx,"key",0,-1).Result()
if err != nil {
	panic(err)
}
fmt.Println(vals)

9. LRem

删除列表中的数据

// 从列表左边开始,删除100, 如果出现重复元素,仅删除1次,也就是删除第一个
dels, err := rdb.LRem(ctx,"key",1,100).Result()
if err != nil {
	panic(err)
}

// 如果存在多个100,则从列表左边开始删除2个100
rdb.LRem(ctx,"key",2,100)


// 如果存在多个100,则从列表右边开始删除2个100
// 第二个参数负数表示从右边开始删除几个等于100的元素
rdb.LRem(ctx,"key",-2,100)

// 如果存在多个100,第二个参数为0,表示删除所有元素等于100的数据
rdb.LRem(ctx,"key",0,100)

10. LIndex

根据索引坐标,查询列表中的数据

// 列表索引从0开始计算,这里返回第6个元素
val, err := rdb.LIndex(ctx,"key",5).Result()
if err != nil {
	panic(err)
}

fmt.Println(val)

11. LInsert

在指定位置插入数据

// 在列表中5的前面插入4
// before是之前的意思
err := rdb.LInsert(ctx,"key","before", 5, 4).Err()
if err != nil {
	panic(err)
}

// 在列表中 zhangsan 元素的前面插入 欢迎你
rdb.LInsert(ctx,"key","before", "zhangsan", "欢迎你")

// 在列表中 zhangsan 元素的后面插入 2022
rdb.LInsert(ctx,"key","after", "zhangsan", "2022")

4. Set

1. SAdd

添加集合元素

// 添加100到集合中
err := rdb.SAdd(ctx,"key",100).Err()
if err != nil {
	panic(err)
}

// 将100,200,300添加到集合中
rdb.SAdd(ctx,"key",100, 200, 300)

2. SCard

获取集合元素个数

size, err := rdb.SCard(ctx,"key").Result()
if err != nil {
	panic(err)
}
fmt.Println(size)

3. SIsMember

判断元素是否在集合中

// 检测100是否包含在集合中
ok, _ := rdb.SIsMember(ctx,"key", 100).Result()
if ok {
	fmt.Println("集合包含指定元素")
}

4. SMembers

获取集合中所有的元素

es, _ := rdb.SMembers(ctx,"key").Result()
// 返回的es是string数组
fmt.Println(es)

5. SRem

删除集合元素

// 删除集合中的元素100
	rdb.SRem(ctx, "key", 100)

	// 删除集合中的元素200和300
	rdb.SRem(ctx, "key", 200, 300)

6. SPop,SPopN

随机返回集合中的元素,并且删除返回的元素

// 随机返回集合中的一个元素,并且删除这个元素
val, _ := rdb.SPop(ctx,"key").Result()
fmt.Println(val)

// 随机返回集合中的5个元素,并且删除这些元素
vals, _ := rdb.SPopN(ctx,"key", 5).Result()
fmt.Println(vals)

5. sorted set

1. ZAdd

添加一个或者多个元素到集合,如果元素已经存在则更新分数

// 添加一个集合元素到集合中, 这个元素的分数是2.5,元素名是zhangsan
err := rdb.ZAdd(ctx, "key", &redis.Z{Score: 2.5, Member: "zhangsan"}).Err()
if err != nil {
		panic(err)
}	

2. ZCard

返回集合元素个数

size, err := rdb.ZCard(ctx,"key").Result()
if err != nil {
	panic(err)
}
fmt.Println(size)

3. ZCount

统计某个分数范围内的元素个数

// 返回: 1<=分数<=5 的元素个数, 注意:"1", "5"两个参数是字符串
size, err := rdb.ZCount(ctx,"key", "1","5").Result()
if err != nil {
	panic(err)
}
fmt.Println(size)

// 返回: 1<分数<=5 的元素个数
// 说明:默认第二,第三个参数是大于等于和小于等于的关系。
// 如果加上( 则表示大于或者小于,相当于去掉了等于关系。
size, err := rdb.ZCount(ctx,"key", "(1","5").Result()

4. ZIncrBy

增加元素的分数

// 给元素zhangsan,加上2分
rdb.ZIncrBy(ctx,"key", 2,"zhangsan")

5.ZRange,ZRevRange

返回集合中某个索引范围的元素,根据分数从小到大排序

// 返回从0到-1位置的集合元素, 元素按分数从小到大排序
// 0到-1代表则返回全部数据
vals, err := rdb.ZRange(ctx,"key", 0,-1).Result()
if err != nil {
	panic(err)
}

for _, val := range vals {
	fmt.Println(val)
}

ZRevRange用法跟ZRange一样,区别是ZRevRange的结果是按分数从大到小排序。

6. ZRangeByScore

根据分数范围返回集合元素,元素根据分数从小到大排序,支持分页。

// 初始化查询条件, Offset和Count用于分页
op := redis.ZRangeBy{
	Min:"2", // 最小分数
	Max:"10", // 最大分数
	Offset:0, // 类似sql的limit, 表示开始偏移量 
	Count:5, // 一次返回多少数据
}
	
vals, err := rdb.ZRangeByScore(ctx,"key", &op).Result()
if err != nil {
	panic(err)
}

for _, val := range vals {
	fmt.Println(val)
}

7. ZRevRangeByScore

用法类似ZRangeByScore,区别是元素根据分数从大到小排序。

8. ZRangeByScoreWithScores

用法跟ZRangeByScore一样,区别是除了返回集合元素,同时也返回元素对应的分数

// 初始化查询条件, Offset和Count用于分页
op := redis.ZRangeBy{
	Min:"2", // 最小分数
	Max:"10", // 最大分数
	Offset:0, // 类似sql的limit, 表示开始偏移量
	Count:5, // 一次返回多少数据
}

vals, err := rdb.ZRangeByScoreWithScores(ctx,"key", &op).Result()
if err != nil {
	panic(err)
}

for _, val := range vals {
	fmt.Println(val.Member) // 集合元素
	fmt.Println(val.Score) // 分数
}

9. ZRem

删除集合元素

// 删除集合中的元素zhangsan
rdb.ZRem(ctx,"key", "zhangsan")

// 删除集合中的元素zhangsan和zhangsan1
// 支持一次删除多个元素
rdb.ZRem(ctx,"key", "zhangsan", "zhangsan1")

10. ZRemRangeByRank

根据索引范围删除元素

// 集合元素按分数排序,从最低分到高分,删除第0个元素到第5个元素。
// 这里相当于删除最低分的几个元素
rdb.ZRemRangeByRank(ctx,"key", 0, 5)

// 位置参数写成负数,代表从高分开始删除。
// 这个例子,删除最高分数的两个元素,-1代表最高分数的位置,-2第二高分,以此类推。
rdb.ZRemRangeByRank(ctx,"key", -1, -2)

11.ZRemRangeByScore

根据分数范围删除元素

// 删除范围: 2<=分数<=5 的元素
rdb.ZRemRangeByScore(ctx,"key", "2", "5")

// 删除范围: 2<=分数<5 的元素
rdb.ZRemRangeByScore(ctx,"key", "2", "(5")

12. ZScore

查询元素对应的分数

// 查询集合元素zhangsan的分数
score, _ := rdb.ZScore(ctx,"key", "zhangsan").Result()
fmt.Println(score)

13. ZRank

根据元素名,查询集合元素在集合中的排名,从0开始算,集合元素按分数从小到大排序

rk, _ := rdb.ZRank(ctx,"key", "zhangsan").Result()
fmt.Println(rk)

ZRevRank的作用跟ZRank一样,区别是ZRevRank是按分数从大到小排序。

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

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

相关文章

并查集,真好用,一次AC不是梦!

文章目录 &#x1f680;前言&#x1f680;并查集&#x1f680;并查集的两个优化✈️路径压缩✈️按秩合并 &#x1f680;并查集代码模板 &#x1f680;前言 大家好啊&#xff01;今天阿辉来给大家介绍一种简洁而优雅的数据结构——并查集&#xff0c;不知道各位是否了解它&…

IC会员卡写编号,写印刷卡号,卡面卡号,加密D3读卡器R330读卡器会员卡系统

IC卡作为会员卡来使用&#xff0c;比磁条卡安全耐用 游戏厅会员卡、酒店会员卡、美容养生会所会员卡等 IC卡片印刷好以后&#xff0c;要把卡面印刷编号写入到卡片中&#xff0c;才可以在会员卡系统上使用 用以下软件可以批量写入 写入以后&#xff0c;我读取卡片内的数据 把数…

旧衣回收小程序开发,市场发展潜力巨大

在当下快节奏生活下&#xff0c;人们的闲置衣物越来越多&#xff0c;为了减少浪费&#xff0c;旧衣回收成成为了大众的选择。旧衣回收能够降低人们的损失&#xff0c;减少我国资源浪费&#xff0c;又能循环利用&#xff0c;是我国一个环保商业模式。 “互联网旧衣回收”推动市场…

【Effective Objective - C】—— block 块

【Effective Objective - C】—— block 块 前言37.理解块的概念块的基础知识块可以捕获变量内联块的用法块的内部结构栈块堆块全局块要点 38.为常用的块类型创建typedef要点 39.用handler块降低代码分散程度协议传值实现异步块实现异步回调操作里的块要点 40.用块引用其所属对…

JAVA高并发——JDK的并发容器

文章目录 1、超好用的工具类&#xff1a;并发集合简介2、线程安全的HashMap3、深入浅出ConcurrentHashMap3.1、ConcurrentHashMap的内部数据结构3.2、put()方法的实现3.3、get()方法的实现 4、有关List的线程安全5、高效读写的队列&#xff1a;深度剖析ConcurrentLinkedQueue类…

USB隔离模块(ADUM4160)--- 保护您的创新:从高精度到高电压

所需设备&#xff1a; 1、ISOUSB 隔离器&#xff1b; 数据信号、电源 双重隔离&#xff1b; ISOUSB 隔离器采用实现单个 USB 输入&#xff0c;单个USB 隔离输出功能&#xff0c;方便实际应用。另外&#xff0c;本产品采用 ADI 公司的 iCoupler 磁耦隔离技术&#xff0c;芯片…

震惊世界的Sora发明者之一,是毕业于上海交大的天才少年-谢赛宁?(本人辟谣)

很少发票圈&#xff0c;如果大家看到这个公众号标题党的离大谱的文章&#xff0c;求一定帮忙点下举报不实信息。如果有认识微信相关部门的朋友也请联系我一下。 Sora是bill他们在openai的呕心之作&#xff0c;我虽然不知道细节&#xff0c;但是bill告诉我他们每天基本不睡觉高强…

多线程——

目录 一、为什么要有多线程&#xff1f; 1、线程与进程 2、多线程的应用场景 3、小结​编辑 二、多线程中的两个概念&#xff08;并发和并行&#xff09; 1、并发 2、并行 3、小结 三、多线程的三种实现方式 1、继承Thread类的方式进行实现 2、实现Runnable接口的方…

【每日一题】三数之和

三数之和&#xff0c;点击左侧即可跳转。 目录 题目详情&#xff1a;思路&#xff1a;暴力&#xff1a;双指针&#xff1a; 代码实现&#xff1a; 题目详情&#xff1a; 思路&#xff1a; 暴力&#xff1a; 我们会显而易见的想到使用暴力解法&#xff0c;3个for循环暴力枚举…

如何在Ubuntu系统使用Docker部署开源白板工具Excalidraw并实现公网访问

文章目录 1. 安装Docker2. 使用Docker拉取Excalidraw镜像3. 创建并启动Excalidraw容器4. 本地连接测试5. 公网远程访问本地Excalidraw5.1 内网穿透工具安装5.2 创建远程连接公网地址5.3 使用固定公网地址远程访问 本文主要介绍如何在Ubuntu系统使用Docker部署开源白板工具Excal…

JavaAgent介绍 | 基本介绍及无侵入打印方法耗时

闲聊 最近在配置skywalking的过程中发现了-javaagent:这个配置&#xff0c;这里做一个简单的学习 什么是JavaAgent 网上似乎没有直接的文档介绍这个&#xff0c;只找了instrument包的相关文档&#xff0c;其内容页。其中的内容也都是介绍javaagent相关。 instrument包下核心…

【深蓝学院】移动机器人运动规划--第5章 最优轨迹生成--笔记

文章目录 1. Preliminaries2. Multicopter dynamics and differential flatness&#xff08;多旋翼动力学和微分平坦特性&#xff09;2.1 Differential Flatness2.2 具体建模2.3 Flatness Transformation的解析推导 3. Trajectory Optimization轨迹优化3.1 Problem formulation…

5年测试被裁,年前恶补3个月上岸字节28K,面试差点被问哭···

我的个人背景非常简单&#xff0c;也可以说丝毫没有亮点。 学历普通&#xff0c;计算机专业二本毕业&#xff0c;毕业后出来就一直在一家小公司&#xff0c;岁月如梭细&#xff0c;算了下至今从事软件测试已经5年了&#xff0c;也点点点了五年&#xff0c;每天都是重复的工作&…

【 JS 进阶 】原型对象、面向对象

目标 了解构造函数原型对象的语法特征&#xff0c;掌握 JavaScript 中面向对象编程的实现方式&#xff0c;基于面向对象编程思想实现 DOM 操作的封装。 了解面向对象编程的一般特征掌握基于构造函数原型对象的逻辑封装掌握基于原型对象实现的继承理解何为原型链及其作用能够处理…

安全测试工具安装指南:在统信UOS上部署Burp Suite

原文链接&#xff1a;安全测试工具安装指南&#xff1a;在统信UOS上部署Burp Suite 大家好&#xff01;在网络安全领域&#xff0c;Burp Suite是一款不可或缺的工具&#xff0c;它提供了从初级映射和分析应用程序攻击面到查找和利用安全漏洞的一系列功能。今天&#xff0c;我将…

Pytorch框架-----torch.tensor(创建张量)

文章目录 前言一、torch.Tensor二、构建tensor1.从Python的list或序列构建2.空张量3.索引和切片来获取和修改一个张量tensor中的内容 前言 torch.Tensor 是包的核心类。如果将其属性 .requires_grad 设置为 True&#xff0c;则会开始跟踪针对 tensor的所有操作。完成计算后&am…

VS引用第三方库

使用Qt开发习惯了&#xff0c;切换来VS环境&#xff0c;居然引用第三方库&#xff0c;都有所不适应&#xff1b;因为之前都是在Qt项目的pro文件里面直接手写配置好第三方库的include目录、lib目录和依赖的lib名称和拷贝dll语句. Vs也一样&#xff0c;如下三步&#xff1a; 第一…

世界顶级名校计算机专业,都在用哪些书当教材?(文末送书)

目录 01《深入理解计算机系统》02《算法导论》03《计算机程序的构造和解释》04《数据库系统概念》05《计算机组成与设计&#xff1a;硬件/软件接口》06《离散数学及其应用》07《组合数学》08《斯坦福算法博弈论二十讲》参与规则 清华、北大、MIT、CMU、斯坦福的学霸们在新学期里…

板块一 Servlet编程:第三节 HttpServletRequest对象全解与请求转发 来自【汤米尼克的JAVAEE全套教程专栏】

板块一 Servlet编程&#xff1a;第三节 HttpServletRequest对象全解与请求转发 一、什么是HttpServletRequest二、接收请求的常用方法三、请求乱码问题四、请求转发&#xff1a;forward五、Request作用域getParameter和getAttribute的区别 在上一节中我们已经学习了完整的Servl…

【Linux】进程地址空间的理解

进程地址空间的理解 一&#xff0c;什么是程序地址空间二&#xff0c;页表和虚拟地址空间三&#xff0c;为什么要有进程地址空间 一&#xff0c;什么是程序地址空间 在我们写程序时&#xff0c;都会有这样下面的内存结构&#xff0c;来存放变量和代码等数据。 一个进程要执行…