Go笔记day7 | 青训营

90 阅读6分钟
    1.chan<- int是一个只能发送的通道,可以发送但是不能接收;
    2.<-chan int是一个只能接收的通道,可以接收但是不能发送。   

Go语言中的并发安全性和锁是实现并发控制的重要概念。下面是关于并发安全和锁的笔记:

  1. 并发安全性:

    • 并发安全性是指在多个并发执行的goroutine中,共享的数据能够被正确地访问和操作,而不会出现数据竞争、内存泄漏或其他意外行为。
    • 在Go语言中,通过使用通道(channel)和互斥锁(mutex)等机制,可以实现并发安全性。
  2. 互斥锁(Mutex):

    • 互斥锁是一种常用的并发控制机制,用于保护共享资源,确保同一时间只有一个goroutine能够访问该资源。

    • Go语言中的sync包提供了互斥锁的实现,可以使用sync.Mutex类型来创建互斥锁对象。

    • 使用互斥锁的基本流程为:

      • 在访问共享资源之前,调用互斥锁的Lock方法获取锁。
      • 在访问完成后,调用互斥锁的Unlock方法释放锁。
  3. 读写锁(RWMutex):

    • 读写锁是一种特殊类型的锁,可以在多个读操作同时进行的情况下,防止写操作的并发访问。

    • Go语言中的sync包提供了读写锁的实现,可以使用sync.RWMutex类型来创建读写锁对象。

    • 使用读写锁的基本流程为:

      • 对于读操作,调用读写锁的RLock方法获取读锁,读取共享资源。
      • 对于写操作,调用读写锁的Lock方法获取写锁,修改共享资源。
      • 在读或写操作完成后,分别调用读写锁的RUnlockUnlock方法释放锁。
  4. 原子操作(Atomic):

    • 原子操作是一种不可分割的操作,可以确保在并发环境中对共享数据的操作是原子的、不会被打断的。
    • Go语言中的sync/atomic包提供了一组原子操作的函数,可以用于对数值类型进行原子操作,如原子增减、加载存储等。

并发安全性和锁是保证多个goroutine安全协同工作的重要手段。在编写并发程序时,需要考虑共享资源的访问限制,选择合适的锁机制来保护共享资源的并发访问,以避免数据竞争和其他并发问题。

以上是关于Go语言中并发安全性和锁的笔记。 channel + goroutine

package main

import (
	"fmt"
	"math/rand"
	"runtime"
	"time"
)

type Job struct {
	Id      int
	Randnum int
}

type Res struct {
	job *Job
	sum int
}

func main() {
	//job管道
	jobChan := make(chan *Job, 123)
	resultChan := make(chan *Res, 123)
	//创建工作池
	createPool(64, jobChan, resultChan)
	//开个打印的协程
	go func(resultChan chan *Res) {
		//遍历结果管道打印
		for res := range resultChan {
			fmt.Printf("job is:%v randnum:%v ans:%d\n", res.job.Id, res.job.Randnum, res.sum)
			time.Sleep(time.Second * 1)
		}
	}(resultChan)
	var id int
	//循环创建job 输入到管道
		defer fmt.Println("job生产结束")
		for {
			id++
			//生成randnum
			r_num := rand.Int()
			job := &Job{
				Id:      id,
				Randnum: r_num,
			}
			jobChan <- job

		}
	time.Sleep(time.Second)
}

// arg1 开的协程的个数
func createPool(num int, jobChan chan *Job, resultChan chan *Res) {
	//根据开的协程的个数,去跑运行
	for i := 0; i < num; i++ {
		go func(jobChan chan *Job, resultChan chan *Res) {
			for job := range jobChan {
				r_num := job.Randnum
				var sum int
				for r_num != 0 {
					tmp := r_num % 10
					sum += tmp
					r_num /= 10
				}
				r := &Res{
					job: job,
					sum: sum,
				}

				resultChan <- r
			}
		}(jobChan, resultChan)
	}
}

time

package main

import (
	"fmt"
	"time"
)

func main() {
	timer1 := time.NewTimer(2 * time.Second)
	t1 := time.Now()
	fmt.Printf("t1:%v\n", t1)
	t2 := <-timer1.C //阻塞进程 直到定时器触发并向通道发送一个时间值
	fmt.Printf("t2:%v\n", t2)
}

并发安全和锁

package main

import (
	"fmt"
	"sync"
)

var x int64
var wg sync.WaitGroup

func add() {
	for i := 0; i < 5000; i++ {
		x++
	}
	wg.Done()
}

func main() {
	wg.Add(2)
	go add()
	go add()
	wg.Wait()
	fmt.Printf("The ans is %v", x)
}

注意 sync包中提供了并发安全的Map var m = sync.Map{} 这个Map的使用不需要初始化key和value的类型,应该是动态初始化。

go中也是有原子操作以及c++中类似的CAS操作,compare_and_swap 举个例子 int63类型的value进行原子+1时使用如下

atomic.Addint64(&value,1)

Redis 操作笔记:

  • Redis 是一个开源的高性能键值存储系统,可以用于缓存、数据库和消息队列等场景。
  • 在 Go 语言中,可以使用第三方的 Redis 客户端库,如 go-redis、redigo 等,来与 Redis 进行交互。
  • 使用 Redis 客户端库,可以进行常见的操作,如设置键值对、获取键值对、删除键、设置过期时间、发布订阅等。

time 库的笔记:

  • time 是 Go 语言标准库提供的用于处理时间和日期的包。
  • time 包提供了各种函数和方法,用于获取当前时间、解析时间字符串、格式化时间、计算时间差等操作。
  • 可以使用 time.Now() 函数获取当前的本地时间;可以使用 time.Parse() 函数将字符串解析为时间对象;可以使用 time.Format() 函数将时间格式化为字符串。
  • time 包还提供了一些时间间隔的类型和常用的操作,如计时器、定时器、睡眠等。
  • 对于时间运算,可以使用 time.Add() 方法来增加或减少时间;使用 time.Since() 方法计算时间差;使用 time.Duration 表示时间间隔。
  • 在处理时间时,需要注意时区、时间格式的选择,以及遵循时间操作的最佳实践,如使用 time.UTC 或 time.Local 设置时区,使用固定的时间格式进行解析和格式化 数据库的操作:redis+go

go连接redis服务器,参数,网络协议和地址

当使用Go语言中的Redis客户端库进行操作时,可以进行以下常见的操作:

  1. 设置键值对:

    • 使用SET命令来设置键值对,将指定的值与指定的键关联起来。例如:SET key value
    • 通过客户端库提供的方法,可以使用类似Set(key, value)的函数来设置键值对。
  2. 获取键值对:

    • 使用GET命令来获取键对应的值。例如:GET key
    • 通过客户端库提供的方法,可以使用类似Get(key)的函数来获取键值对的值。
  3. 删除键:

    • 使用DEL命令来删除指定的键。例如:DEL key
    • 通过客户端库提供的方法,可以使用类似Del(key)的函数来删除指定的键。
  4. 设置过期时间:

    • 使用EXPIRE命令来设置键的过期时间。例如:EXPIRE key seconds
    • 通过客户端库提供的方法,可以使用类似Expire(key, duration)的函数来设置键的过期时间。
  5. 发布订阅:

    • Redis支持发布订阅模型,可以通过PUBLISH命令发布消息,通过SUBSCRIBE命令订阅消息。
    • 通过客户端库提供的方法,可以使用类似Publish(channel, message)Subscribe(channel)的函数来实现消息的发布和订阅。

除了上述操作外,Redis还支持更多高级功能,如哈希表操作、列表操作、集合操作、事务等。可以根据具体需求使用相应的命令或客户端库提供的方法进行操作。

package main

import (
	"fmt"

	"github.com/garyburd/redigo/redis"
)

func main() {
	c, err := redis.Dial("udp", "127.0.0.1:30000")
	if err != nil {
		fmt.Println("conn redis failed,", err)
		return
	}

	fmt.Println("redis conn success")

	defer c.Close()
}

设置字段的过期时间

	_, _ = c.Do("expire", "moxu", 10)

Redis还有Redia连接池的操作,类似于线程池 使用的时候进行取 使用完成后进行归还