面试题:给一个数字n,请你用2个线程分别输出奇数和偶数,要求最终输出顺序是递增的


题目描述

请你用你熟悉的语言,实现:给一个数字n,用2个线程分别输出1,3,5,7,9,…和2,4,6,8,10,…,一直到n,但最终输出的结果是1,2,3,4,5,6,7,8,9,10,…,n

示例

n=100

结果:1,2,3,…,99,100

代码

func PrintN(n int) {
	// 定义2个chan,分别控制2个go runtine运行
	r1, r2 := make(chan bool, 1), make(chan bool, 1)
	// 定义结束chan
	stop := make(chan bool, 2)
	// 启动第一个go runtine
	go func() {
		r1 <- true
	}()
	// go runtine1
	go func(i int) {
		for {
			select {
			case <-r1:
				fmt.Printf("%d,", i)
				i += 2
				r2 <- true
				if i >= n {
					stop <- true
					return
				}
			}
		}
	}(1)

	// go runtine2
	go func(i int) {
		for {
			select {
			case <-r2:
				fmt.Printf("%d,", i)
				i += 2
				r1 <- true
				if i > n {
					stop <- true
					return
				}
			}
		}
	}(2)
	// 监听结束信号
	<-stop
	<-stop
	// 关闭chan
	close(r1)
	close(r2)
	close(stop)
}

结果:

当n=100

1,2,3,4,5,6,7,8,9,10,11,12,13,14,15,16,17,18,19,20,21,22,23,24,25,26,27,28,29,30,31,32,33,34,35,36,37,38,39,40,41,42,43,44,45,46,47,48,49,50,51,52,53,54,55,56,57,58,59,60,61,62,63,64,65,66,67,68,69,70,71,72,73,74,75,76,77,78,79,80,81,82,83,84,85,86,87,88,89,90,91,92,93,94,95,96,97,98,99,100,101,102,103,104,105,106,107,108,109,110,111,112,113,114,115,116,117,118,119,120,121,122,123,124,125,126,127,128,129,130,131,132,133,134,135,136,137,138,139,140,141,142,143,144,145,146,147,148,149,150,151,152,153,154,155,156,157,158,159,160,161,162,163,164,165,166,167,168,169,170,171,172,173,174,175,176,177,178,179,180,181,182,183,184,185,186,187,188,189,190,191,192,193,194,195,196,197,198,199,200,

分析

题目看起来很简单,当你真正上手写代码的时候很容易遇到各种问题。建议自己动手试一试。

当然面试官的题目不会这么简单,这个时候会对你的代码提出一些要求,比如,只用一个chan怎么实现呢?(如果你一开始就想到一个chan实现那很棒哦)

用一个chan实现

func PrintN(n int) {
	// 定义chan
	run := make(chan bool)
	// 定义结束chan
	stop := make(chan bool, 2)
	// go runtine1
	go func(i int) {
		for {
			fmt.Printf("%d,", i)
			i += 2
			run <- true
			if i >= n {
				stop <- true
				return
			}
		}
	}(1)

	// go runtine2
	go func(i int) {
		for {
			<-run
			fmt.Printf("%d,", i)
			i += 2
			if i > n {
				stop <- true
				return
			}
		}
	}(2)
	// 监听结束信号
	<-stop
	<-stop
	// 关闭chan
	close(run)
	close(stop)
}

当n=100,结果如下

1,3,2,4,5,7,6,8,9,11,10,12,13,15,14,16,17,19,18,20,21,23,22,24,25,27,26,28,29,31,30,32,33,35,34,36,37,39,38,40,41,43,42,44,45,47,46,48,49,51,50,52,53,55,54,56,57,59,58,60,61,63,62,64,65,67,66,68,69,71,70,72,73,75,74,76,77,79,78,80,81,83,82,84,85,87,86,88,89,91,90,92,93,95,94,96,97,99,98,100

看起来是对的,当你仔细一想,发现1,3,2,4,5,7,6,…什么鬼,竟然是错的。仔细阅读代码发现,当go2<-run代码接收到go1发的信号并开始执行fmt.Printf("%d,", i)的时候,此时go1run <- true代码也停止阻塞并且继续执行,因此go1也可以执行fmt.Printf("%d,", i)了,这个时候go1可能在go2前面执行fmt.Printf("%d,", i),因此就出现了1,3,2这种情况,改如何解决呢?

我们将代码稍微做一点改动

func PrintN(n int) {
	// 定义chan
	run := make(chan bool)
	// 定义结束chan
	stop := make(chan bool, 2)
	// go runtine1
	go func(i int) {
		for {
			fmt.Printf("%d,", i)
			i += 2
			run <- true
			run <- true
			if i >= n {
				stop <- true
				return
			}
		}
	}(1)

	// go runtine2
	go func(i int) {
		for {
			<-run
			fmt.Printf("%d,", i)
			i += 2
			<-run
			if i > n {
				stop <- true
				return
			}
		}
	}(2)
	// 监听结束信号
	<-stop
	<-stop
	// 关闭chan
	close(run)
	close(stop)
}

改动点:

  • go2 在执行fmt.Printf("%d,", i)后再监听一次run
  • go1 连续2次向run中写入数据

逻辑分析:go2监听2次run,第一次监听完了表示go1还不能执行,要等go2执行完fmt.Printf("%d,", i)后,go1再写一次run,go2监听到第二次run后,表示go2执行完成了,go1可以执行了。

总结

每日学习一点,日积月累


文章作者: Alex
版权声明: 本博客所有文章除特別声明外,均采用 CC BY 4.0 许可协议。转载请注明来源 Alex !
  目录