go语言特性代码

1 开启100个协程,顺序打印1-1000,且保证协程号1的,打印尾数为1的数字

//  同时开启100个协程(分别为1号协程 2号协程 ... 100号协程,
//  1号协程只打印尾数为1的数字,2号协程只打印尾数为2的数,
//   以此类推),请顺序打印1-1000整数以及对应的协程号;

func goCode01() {
	s := make(chan struct{})
	var wg sync.WaitGroup
	m := make(map[int]chan int, 100)
	for i := 1; i <= 100; i++ {
		m[i] = make(chan int)
	}
	for i := 1; i <= 100; i++ {
		wg.Add(1)
		go func(i int) {
			defer wg.Done()
			for {
				num, ok := <-m[i]
				// ok标记:false代表通道关闭
				if !ok {
					fmt.Printf("子协程 %d 号: 通道关闭 , exit\n", i)
					break // 通道关闭,退出循环,goroutine结束
				}
				fmt.Printf("子协程 %d 打印: %d\n", i, num)
				// 收到数字,通知主协程
				s <- struct{}{}
			}
		}(i)
	}

	// 投递任务 1~1000
	for i := 1; i <= 1000; i++ {
		id := i % 100
		if id == 0 {
			id = 100
		}
		m[id] <- i // 往通道传递数据,然后阻塞
		<-s        //子协程收到数据了,通知主协程
	}

	// 任务完成,关闭所有子协程通道
	for _, ch := range m {
		close(ch)
	}
	// 等待所有worker退出,替代sleep
	wg.Wait()
	fmt.Println("main exit")
}

2 三个goroutinue交替打印abc 10次

func goCode02() {
	cha := make(chan struct{})
	chb := make(chan struct{})
	chc := make(chan struct{})
	var wg sync.WaitGroup
	times := 10
	wg.Add(3)
	go func() {
		defer wg.Done()
		for i := 1; i <= times; i++ {
			<-cha
			fmt.Printf("协程1打印第%d个a\n", i)
			// 通知协程2打印
			chb <- struct{}{}
		}
		// 再专门接收 C 最后多出来的这个信号。不然协程3会阻塞
		<-cha
	}()

	go func() {
		defer wg.Done()
		for i := 1; i <= times; i++ {
			<-chb
			fmt.Printf("协程2打印第%d个b\n", i)
			// 通知协程3打印
			chc <- struct{}{}
		}
	}()

	go func() {
		defer wg.Done()
		for i := 1; i <= times; i++ {
			<-chc
			fmt.Printf("协程3打印第%d个c\n", i)
			// 通知协程1打印
			cha <- struct{}{}
		}
	}()
	// 启动:先给A发第一个信号,开始第一轮
	cha <- struct{}{}
	wg.Wait()
	fmt.Println("全部打印完成")
}

3 用不超过10个goroutine不重复的打印slice中的100个元素

func goCode03() {
	var wg sync.WaitGroup
	// 构造100个元素
	data := make([]int, 100)
	for index := range data {
		data[index] = index
	}
	// 创建带缓存通道,通道缓存20个数据
	s := make(chan int, 20)
	workNum := 10
	for i := 1; i <= workNum; i++ {
		wg.Add(1)
		go func(i int) {
			defer wg.Done()
			for {
				// 接收数据
				num, ok := <-s
				if !ok {
					fmt.Printf("子协程%d 通道关闭 \n", i)
					break
				}
				fmt.Printf("子协程%d 打印%d \n", i, num)
			}

		}(i)
	}
	go func() {
		// 往通道发数据
		for _, v := range data {
			s <- v
		}
		// 发完数据,通道关闭
		close(s)
	}()

	wg.Wait()
	println("主协程关闭")
}

4 两个协程交替打印奇偶数

func goCode04() {
	chOdd := make(chan struct{})  // 奇数通道
	chEven := make(chan struct{}) // 偶数通道
	var wg sync.WaitGroup
	wg.Add(2)
	go func() {
		defer wg.Done()
		for i := 0; i <= 100; i++ {
			<-chOdd
			if i%2 == 1 {
				fmt.Printf("奇数:%d\n", i)
			}
			chEven <- struct{}{}
		}
		<-chOdd
	}()
	go func() {
		defer wg.Done()
		for i := 0; i <= 100; i++ {
			<-chEven
			if i%2 == 0 {
				fmt.Printf("偶数:%d\n", i)
			}
			chOdd <- struct{}{}
		}
	}()

	// 启动,先唤醒打印奇数的协程
	chOdd <- struct{}{}
	wg.Wait()
	fmt.Println("打印完成")
}

5 用单个channel实现0,1的交替打印

func goCode05() {
	s := make(chan struct{})
	go func() {
		for {
			<-s
			fmt.Printf("协程a打印0\n")
			s <- struct{}{}
		}

	}()
	go func() {
		for {
			<-s
			fmt.Printf("协程b打印1\n")
			s <- struct{}{}
		}

	}()
	s <- struct{}{}
	time.Sleep(3 * time.Second)
}

6 sync.Cond实现多生产者多消费者

package main

import (
    "context"
    "fmt"
    "math/rand"
    "sync"
    "time"
)

func main() {
    var wg sync.WaitGroup
    var cond sync.Cond
    cond.L = new(sync.Mutex)
    msgCh := make(chan int, 5)
    ctx, cancel := context.WithTimeout(context.Background(), 3*time.Second)
    defer cancel()
    rand.Seed(time.Now().UnixNano())

    // 生产者
    producer := func(ctx context.Context, out chan<- int, idx int) {
       defer wg.Done()
       for {
          select {
          case <-ctx.Done():
             // 每次生产者退出,都唤醒一个消费者处理,防止最后有消费者线程死锁
             // 生产者比消费者多,所以cond.Signal()就可以。不然的话建议Broadcast()
             cond.Broadcast()
             fmt.Println("producer finished")
             return
          default:
             cond.L.Lock()
             for len(msgCh) == 5 {
                cond.Wait()
             }
             num := rand.Intn(500)
             out <- num
             fmt.Printf("producer: %d, msg: %d\n", idx, num)
             cond.Signal()
             cond.L.Unlock()
          }
       }
    }

    // 消费者
    consumer := func(ctx context.Context, in <-chan int, idx int) {
       defer wg.Done()
       for {
          select {
          case <-ctx.Done():
             // 消费者可以选择继续消费直到channel为空
             for len(msgCh) > 0 {
                select {
                case num := <-in:
                   fmt.Printf("consumer %d, msg: %d\n", idx, num)
                default:
                   // 如果channel已经空了,跳出循环
                   break
                }
             }
             fmt.Println("consumer finished")
             return
          default:
             cond.L.Lock()
             for len(msgCh) == 0 {
                cond.Wait()
             }
             num := <-in
             fmt.Printf("consumer %d, msg: %d\n", idx, num)
             cond.Signal()
             cond.L.Unlock()
          }
       }
    }

    // 启动生产者和消费者
    for i := 0; i < 5; i++ {
       wg.Add(1)
       go producer(ctx, msgCh, i+1)
    }
    for i := 0; i < 3; i++ {
       wg.Add(1)
       go consumer(ctx, msgCh, i+1)
    }

    // 模拟程序运行一段时间
    wg.Wait()
    close(msgCh)
    fmt.Println("all finished")
}

7 使用go实现1000个并发控制并设置执行超时时间1秒

func goCode06() {
	const (
		totalTask     = 1000            // 总任务数量
		concurrency   = 100             // 最大并发数,即控制同时跑多少goroutine
		globalTimeout = 1 * time.Second // 1秒
	)
	ctx, cancle := context.WithTimeout(context.Background(), globalTimeout)
	defer cancle()

	taskCh := make(chan int, totalTask)
	pool := make(chan struct{}, concurrency) //  并发控制令牌池
	var wg sync.WaitGroup

	for i := 1; i <= totalTask; i++ {
		wg.Add(1)
		go func(i int) {
			defer wg.Done()
			for {
				select {
				case <-ctx.Done():
					// 超时/取消,worker退出
					fmt.Println("worker 收到超时信号退出")
					return
				case id, ok := <-taskCh:
					if !ok {
						// 任务通道关闭,没有任务了,退出
						return
					}
					// 获取令牌,占用一个并发名额 pool 有100个控制并发
					pool <- struct{}{}
					fmt.Printf("协程%d获取令牌\n", i)
					goCode06DoTask(id)
					<-pool
				}
			}
		}(i)

	}

	// 生产者:往通道投递任务
producer:
	for i := 0; i <= totalTask; i++ {
		select {
		case <-ctx.Done():
			fmt.Println("任务投递超时,停止生产任务")
			break producer
		case taskCh <- i:
		}
	}

	close(taskCh) // 关闭任务通道,任务发配完成。
	wg.Wait()

	if ctx.Err() != nil {
		fmt.Printf("任务结束,原因:%v\n", ctx.Err())
	} else {
		fmt.Println("所有任务全部正常执行完成")
	}

}
func goCode06DoTask(id int) {
	time.Sleep(10 * time.Millisecond)
	fmt.Printf("协程执行任务:%d\n", id)
}

8 使用两个Goroutine,向标准输出中按顺序按顺序交替打出字母与数字,输出是a1b2c3

func goCode07() {
	chLetter := make(chan struct{})
	chNum := make(chan struct{})
	var wg sync.WaitGroup
	wg.Add(2)
	go func() {
		defer wg.Done()
		for i := 'a'; i <= 'z'; i++ {
			<-chLetter
			fmt.Printf("%s\n", string(i))
			chNum <- struct{}{}
		}
		<-chLetter
	}()
	go func() {
		defer wg.Done()
		for i := 1; i <= 26; i++ {
			<-chNum
			fmt.Printf("%d\n", i)
			chLetter <- struct{}{}
		}
	}()
	chLetter <- struct{}{}
	wg.Wait()
}

9 编写一个程序限制10个goroutine执行,每执行完一个goroutine就放一个新的goroutine进来

func goCode08() {
	const maxConcurrency = 10 // 限制最多10个并发
	const totalTask = 20      // 总共有20个任务

	token := make(chan struct{}, maxConcurrency) //令牌池,容量10
	var wg sync.WaitGroup

	for i := 0; i < totalTask; i++ {
		token <- struct{}{} // 获取令牌,满了这里就阻塞,控制最多10个并发
		wg.Add(1)

		go func(taskId int) {
			defer func() {
				<-token // 任务执行完毕,释放令牌!释放之后,外面循环才能继续进下一个
				wg.Done()
			}()

			// 模拟业务执行
			fmt.Printf("任务 %d 开始执行\n", taskId)
			time.Sleep(1 * time.Second)
			fmt.Printf("任务 %d 执行完成\n", taskId)
		}(i)
	}

	wg.Wait()
	close(token)
	fmt.Println("所有任务执行完毕")
}