Skip to content

Latest commit

 

History

History
279 lines (246 loc) · 4.94 KB

File metadata and controls

279 lines (246 loc) · 4.94 KB

1.思考以下程序在并发中出现panic的可能是什么?并给出解决方案

package main

import (
	"fmt"
	"time"
)

type message struct {
	Topic     string
	Partition int32
	Offset    int64
}

type FeedEventDM struct {
	Type    string
	UserID  int
	Title   string
	Content string
}

type MSG struct {
	ms        message
	feedEvent FeedEventDM
}

const ConsumeNum = 5

func main() {
	var consumeMSG []MSG
	var lastConsumeTime time.Time // 记录上次消费的时间
	msgs := make(chan MSG)

	//这里源源不断的生产信息
	go func() {
		for i := 0; ; i++ {
			msgs <- MSG{
				ms: message{
					Topic:     "消费主题",
					Partition: 0,
					Offset:    0,
				},
				feedEvent: FeedEventDM{
					Type:    "grade",
					UserID:  i,
					Title:   "成绩提醒",
					Content: "您的成绩是xxx",
				},
			}
			//每次发送信息会停止0.01秒以模拟真实的场景
			time.Sleep(100 * time.Millisecond)
		}
	}()

	//不断接受消息进行消费
	for msg := range msgs {
		// 添加新的值到events中
		consumeMSG = append(consumeMSG, msg)
		// 如果数量达到额定值就批量消费
		if len(consumeMSG) >= ConsumeNum {
			//进行异步消费
			go func() {
				m := consumeMSG[:ConsumeNum]
				fn(m)
			}()
			// 更新上次消费时间
			lastConsumeTime = time.Now()
			// 清除插入的数据
			consumeMSG = consumeMSG[ConsumeNum:]
		} else if !lastConsumeTime.IsZero() && time.Since(lastConsumeTime) > 5*time.Minute {
			// 如果距离上次消费已经超过5分钟且有未处理的消息
			if len(consumeMSG) > 0 {
				//进行异步消费
				go func() {
					m := consumeMSG[:ConsumeNum]
					fn(m)
				}()
				// 更新上次消费时间
				lastConsumeTime = time.Now()
				// 清空插入的数据
				consumeMSG = consumeMSG[ConsumeNum:]
			}
		}
	}
}

func fn(m []MSG) {
	fmt.Printf("本次消费了%d条消息\n", len(m))
}

在主协程和消费协程共享一个consumeMSG,在添加新值时,会改变消费切片,导致报错,应该加一个互斥锁将两者的行为分开

修改后如下

package main

import (
	"fmt"
	"sync"
	"time"
)

type message struct {
	Topic     string
	Partition int32
	Offset    int64
}

type FeedEventDM struct {
	Type    string
	UserID  int
	Title   string
	Content string
}

type MSG struct {
	ms        message
	feedEvent FeedEventDM
}

const ConsumeNum = 5

var lock sync.Mutex

func main() {
	var consumeMSG []MSG
	var lastConsumeTime time.Time
	msgs := make(chan MSG)

	go func() {
		for i := 0; ; i++ {
			lock.Lock()
			msgs <- MSG{
				ms: message{
					Topic:     "消费主题",
					Partition: 0,
					Offset:    0,
				},
				feedEvent: FeedEventDM{
					Type:    "grade",
					UserID:  i,
					Title:   "成绩提醒",
					Content: "您的成绩是xxx",
				},
			}
			lock.Unlock()
			time.Sleep(100 * time.Millisecond)
		}
	}()

	for msg := range msgs {
		lock.Lock()
		consumeMSG = append(consumeMSG, msg)
		if len(consumeMSG) >= ConsumeNum {
			m := consumeMSG[:ConsumeNum]
			lastConsumeTime = time.Now()
			consumeMSG = consumeMSG[ConsumeNum:]
			lock.Unlock()
			go func() {
				fn(m)
			}()
		} else if !lastConsumeTime.IsZero() && time.Since(lastConsumeTime) > 5*time.Minute {
			if len(consumeMSG) > 0 {
				m := consumeMSG[:ConsumeNum]
				lastConsumeTime = time.Now()
				consumeMSG = consumeMSG[ConsumeNum:]
				lock.Unlock()
				go func() {
					fn(m)
				}()
			}
		} else {
			lock.Unlock()
		}
	}
}

func fn(m []MSG) {
	fmt.Printf("本次消费了%d条消息\n", len(m))
}

2.使用for循环生成20个goroutine,并向一个channel传入随机数和goroutine编号,等待这些goroutine都生成完后,想办法给这些goroutine按照编号进行排序(输出排序前和排序后的结果,要求不使用额外的空间存储着20个数据)

package main

import (
	"fmt"
	"golang.org/x/exp/rand"
	"sync"
)

type nums struct {
	id  int
	num int
}

var wg sync.WaitGroup

func main() {
	ch := make(chan nums, 20)
	for i := 1; i < 21; i++ {
		wg.Add(1)
		go func(id int) {
			defer wg.Done()
			v := rand.Intn(100)
			ch <- nums{i, v}
		}(i)
		fmt.Printf("%v ", <-ch)
	}
	fmt.Println()
	wg.Wait()
}

3.经典老题:交叉打印下面两个字符串(要求一个打印完,另一个会继续打印) "ABCDEFGHIJKLMNOPQRSTUVWXYZ" "0123..." 得到:"AB01CD23EF34..."

package main

import (
	"fmt"
	"sync"
)

func main() {
	str1 := "ABCDEFGHIJKLMNOPQRSTUVWXYZ"
	str2 := "0123456789"

	ch1 := make(chan byte)
	ch2 := make(chan byte)

	var wg sync.WaitGroup
	wg.Add(2)

	go func() {
		defer wg.Done()
		for i := range str1 {
			ch1 <- str1[i]
		}
		close(ch1)
	}()

	go func() {
		defer wg.Done()
		for i := range str2 {
			ch2 <- str2[i]
		}
		close(ch2)
	}()
	go func() {
		wg.Wait()
	}()

	var a, b byte
	var o1, o2 bool
	for {
		select {
		case a, o1 = <-ch1:
			if !o1 {
				ch1 = nil
			} else {
				fmt.Printf(string(a))
			}
		case b, o2 = <-ch2:
			if !o2 {
				ch2 = nil
			} else {
				fmt.Printf(string(b))
			}
		}

		if ch1 == nil && ch2 == nil {
			break
		}
	}
}