Golang协程常见面试题

2023-05-16

协程面试题

  • 交替打印奇数和偶数
  • N个协程打印1到maxVal
  • 交替打印字符和数字
  • 交替打印字符串
  • 三个协程打印ABC
  • Channel练习

交替打印奇数和偶数

下面让我们一起来看看golang当中常见的算法面试题
使用两个goroutine交替打印1-100之间的奇数和偶数, 输出时按照从小到大输出.
方法一:使用无缓冲的channel进行协程间通信

package main

import (
	"fmt"
	"sync"
)

// PrintOddAndEven1 /*
func PrintOddAndEven1() {
	//方法一,使用无缓冲的channel进行通信
	var wg = new(sync.WaitGroup) //注意这里需要是指针go语言当中都是值传递
	wg.Add(2)
	ch := make(chan struct{}) //无缓冲channel
	defer close(ch)
	maxVal := 100
	go func() {
		defer wg.Done()
		for i := 1; i <= maxVal; i++ {
			ch <- struct{}{}
			if i%2 == 1 { //奇数
				fmt.Printf("the odd is %d\n", i)

			}
		}
	}()

	go func() {
		defer wg.Done()
		for i := 1; i <= maxVal; i++ {
			<-ch          //从管道当中读取一个数据
			if i%2 == 0 { //偶数
				fmt.Printf("the even is %d\n", i)

			}
		}
	}()
	wg.Wait()

}
func main() {
	PrintOddAndEven1()
	fmt.Println("over")
}

下面博主来解释一下这个的原理 首先因为变量ch是一个无缓冲的channel, 所以只有读写同时就绪时才不会阻塞。所以两个goroutine会同时进入各自的 if 语句(此时 i 是相同的),但是此时只能有一个 if 是成立的,不管goroutine快,都会由于读channel或写channel导致阻塞,因此程序会交替打印1-100且有顺序。

方法二:使用有缓冲的channel

func PrintOddAndEven2() {
	var wg = new(sync.WaitGroup) //注意这里需要是指针go语言当中都是值传递
	wg.Add(2)
	oddChan := make(chan struct{}, 1)
	eveChan := make(chan struct{}, 1)
	defer close(oddChan)
	defer close(eveChan)
	oddChan <- struct{}{}
	maxVal := 20
	go func() { //奇数协程
		defer wg.Done()
		for i := 1; i <= maxVal; i += 2 {
			<-oddChan
			fmt.Printf("the odd print %d\n", i)
			eveChan <- struct{}{} //通知偶数协程
		}
	}()

	go func() {
		//偶数协程
		defer wg.Done()
		for i := 2; i <= maxVal; i += 2 {
			<-eveChan
			fmt.Printf("the even print %d\n", i)
			oddChan <- struct{}{} //通知奇数协程可以打印了
		}
	}()
	wg.Wait()

}

func main() {
	PrintOddAndEven2()
	fmt.Println("over")
}

第二个方法使用这个有缓冲的channel。有缓冲的channel当容量没有达到上限时写入不会阻塞在这里奇数协程的channel容量为1我们提前给他写入了一个数据因此当偶数和奇数协程都开始读取数据时,首先读取到数据的是奇数协程,奇数协程打印完之后在通知偶数协程打印,偶数协程打印完成之后在通知奇数协程重复下去就实现了交替打印的效果。

N个协程打印1到maxVal

题目描述非常的简单就是N个协程交替打印1到maxVal。比如N=3,maxVal是这个100效果应该是第一个协程打印1,第二个协程打印2,第三个协程打印3,第一个协程打印4这样的效果。
这道题看起来非常的复杂,博主第一次看到这个题的时候也感觉很复杂但是仔细想一下其实并没有那么复杂和上面两题的解题思路是一样的。下面我们看看这个代码如何实现

package main

import (
	"fmt"
	"sync"
)

func main() {
	maxVal := 10
	res := 0                        //用于打印数字
	N := 3                          //协程的数量
	exitChan := make(chan struct{}) //用于退出
	chanArr := make([]chan struct{}, N)
	for i := 0; i < N; i++ {
		//使用无缓冲的channel
		chanArr[i] = make(chan struct{}, 1)
	}
	num := 0 //记录轮到那个协程开始打印了
	chanArr[0] <- struct{}{}
	for i := 0; i < N; i++ {
		go func(i int) {
			for {
				<-chanArr[i]
				if res >= maxVal {
					exitChan <- struct{}{}
					break
				}
				fmt.Printf("第%d个协程打印%d\n", i, res)
				if num == N-1 {//已经循环一轮了轮到第0个协程打印数据了
					num = 0
				} else {
					num++
				}
				res++
				chanArr[num] <- struct{}{} //第num个协程可以打印数据了

			}

		}(i)
	}
	<-exitChan
	for i := 0; i < N; i++ {
		close(chanArr[i]) //将管道全部关闭否则会有协程泄漏
	}

}

其实也非常的简单也是利用channel来进行这个协程之间的通信,由于是N个协程之间进行通信所以了我们定义一个channel的切片首先往第一个channel当中写入一个数据其他管道没有写入数据那么最先打印的一定是这个第一个协程然后我们在利用一个计数器通知其他协程打印。最后需要注意的是主协程退出时需要将管道全部关闭否则其他协程一致阻塞在那里就会引起协程泄漏,就只能等到gc的时候才能回收。

交替打印字符和数字

问题描述: 使用两个 goroutine 交替打印序列,一个 goroutinue 打印数字, 另外一个goroutine打印字母, 最终效果如下 12AB34CD56EF78GH910IJ 。
如果铁子们上面两题会了那么这道题就是有手就行的那种和第一道题没有啥区别

func main() {
	numChan := make(chan struct{}, 1)
	chChan := make(chan struct{}, 1)
	defer close(numChan)
	defer close(chChan)
	var wg sync.WaitGroup
	wg.Add(2)
	numChan <- struct{}{}
	go func() {
		defer wg.Done()
		for num := 1; num <= 26; num++ {
			<-numChan
			fmt.Printf("%d", num)
			chChan <- struct{}{}
		}
	}()

	go func() {
		defer wg.Done()
		for ch := 'A'; ch <= 'Z'; ch++ {
			<-chChan
			fmt.Printf("%s", string(ch))
			numChan <- struct{}{}
		}
	}()
	wg.Wait()

}

同样的也是利用这个channe进行通信,利用有缓冲的channel进行通信。当然也能使用这个无缓冲的channel进行通信

func main() {
	numChan := make(chan struct{})
	defer close(numChan)
	var wg sync.WaitGroup
	wg.Add(2)
	go func() {
		defer wg.Done()
		for num := 1; num <= 26; num++ {
			numChan <- struct{}{}
			fmt.Printf("%d", num)

		}
	}()

	go func() {
		defer wg.Done()
		for ch := 'A'; ch <= 'Z'; ch++ {
			<-numChan
			fmt.Printf("%s", string(ch))
		}
	}()
	wg.Wait()

交替打印字符串

题目描述,给定一个字符串使用两个协程交替打印它。
如果老铁们上面的拿到题都会了这道题不就是和第一道题是这个一模一样的吗?废话不多说直接上代码
方法一使用无缓冲的channel


func main() {
	chChan := make(chan struct{})
	defer close(chChan)
	var wg = new(sync.WaitGroup)
	wg.Add(2)

	str := "hello world"
	N := len(str)
	go func() {
		defer wg.Done()
		for i := 0; i < N; i++ {
			chChan <- struct{}{}
			if i%2 == 0 {
				fmt.Println(string(str[i]))
			}
		}
	}()

	go func() {
		defer wg.Done()
		for i := 0; i < N; i++ {
			<-chChan
			if i%2 == 1 {
				fmt.Println(string(str[i]))
			}
		}
	}()
	wg.Wait()

}

当然也可以使用有缓冲的channel在这里铁子们可以自行编写上面写的太多了。

三个协程打印ABC

题目描述使用三个协程分别打印A,B,C打印这个100次。
本题的难度和上面那几个题完全是一个货色,我们可以使用三个有缓冲的channel就可以达到目的了。具体细节请看代码

package main

import (
	"fmt"
	"sync"
)

func main() {
	Achan := make(chan struct{}, 1)
	Bchan := make(chan struct{}, 1)
	Cchan := make(chan struct{}, 1)
	defer close(Achan)
	defer close(Bchan)
	defer close(Cchan)
	Achan <- struct{}{}
	counter := 0
	maxVal := 10
	exitChan := make(chan struct{}) //用于退出
	go func() {
		for {
			<-Achan
			if counter >= maxVal {
				exitChan <- struct{}{}
				break
			}
			fmt.Printf("%s ", "A")
			counter++
			Bchan <- struct{}{}
		}
	}()

	go func() {
		for {
			<-Bchan
			if counter >= maxVal {
				exitChan <- struct{}{}
				break
			}
			fmt.Printf("%s ", "B")
			counter++
			Cchan <- struct{}{}
		}
	}()

	go func() {
		for {
			<-Cchan
			if counter >= maxVal {
				exitChan <- struct{}{}
				break
			}
			fmt.Printf("%s ", "C")
			counter++
			Achan <- struct{}{}
		}
	}()

	<-exitChan
}

在这里需要注意的点是我们需要close掉这个管道当达到临界值时,主协程退出但是defer方法会执行这个时候管道一关闭所有协程都会收到退出信号,另外两个阻塞在那里的协程就会退出这样就没有这个协程泄漏了。

##并发将多个文件合并到一个文件当中
MergeFile 把多个文件合成一个文件,并发实现子协程优雅退出。采用并发的方式
本题的解题思路同样的也非常的简单我们可以定义一个管道并发的读取文件写入到管道当中然后再并发的写入到文件当中。非常的简单

package main

import (
	"bufio"
	"fmt"
	"io"
	"os"
	"strconv"
	"sync"
)

// MergeFile 把多个文件合成一个文件,并发实现子协程优雅退出

var fileChan = make(chan string, 10000)
var writeFish = make(chan struct{})
var wg sync.WaitGroup

func readFile(fileName string) {
	fin, err := os.Open(fileName)
	if err != nil {
		fmt.Println(err.Error())
		return
	}

	defer fin.Close()
	defer wg.Done()
	reader := bufio.NewReader(fin)
	for {
		line, err := reader.ReadString('\n') //注意已经包含换行符了
		if err != nil {
			if err == io.EOF {
				if len(line) > 0 {
					line += "\n"
					fileChan <- line
				}
				break
			} else {
				fmt.Println(err)
				break
			}
		} else if line == "\r\n" {
			fmt.Println("进来")
			continue
		} else {
			fileChan <- line
		}
	}

}
func writeFile(fileName string) {
	fout, err := os.OpenFile(fileName, os.O_RDWR|os.O_CREATE|os.O_TRUNC, 0644)
	if err != nil {
		fmt.Println(err)
		return
	}
	defer fout.Close()
	defer func() {
		close(writeFish)
	}()
	writer := bufio.NewWriter(fout)
	//LOOP:
	//	for {
	//		select {
	//		case <-readFish:
	//			close(fileChan)//注意需要关闭因为已经没有人往里面写了
	//			for line:=range fileChan{
	//				writer.WriteString(line) //读取时候已经包含换行符了
	//			}
	//			break LOOP
	//		case line := <-fileChan:
	//			writer.WriteString(line) //读取时候已经包含换行符了
	//		}
	//
	//	}
	for {
		if line, ok := <-fileChan; ok {
			if line != "\r\n" {
				writer.WriteString(line)
			}
		} else {
			break
		}
	}
	writer.Flush() //刷新

}

func main() {
	wg.Add(3)
	for i := 1; i <= 3; i++ {
		fileName := "Dir/" + strconv.Itoa(i)
		go readFile(fileName)
	}

	go writeFile("Dir/merge")
	wg.Wait()
	close(fileChan)
	<-writeFish

}

Channel练习

启动一个协程生成100个数发送到ch1管道当中,再启动一个协程从ch1当中取值然后计算平方将其放入ch2管道当中主协程打印

package main

import (
	"fmt"
	"sync"
)

var wg sync.WaitGroup

func f1(ch1 chan int) {
	defer wg.Done()
	for i := 0; i < 50; i++ {
		ch1 <- i
	}
	close(ch1)
}
func f2(ch2 chan int, ch1 chan int) {
	defer wg.Done()
	defer close(ch2)
	for x := range ch1 {
		ch2 <- x * x
	}
}
func main() {
	wg.Add(2)
	a := make(chan int, 50)
	b := make(chan int, 50)
	go f1(a)
	go f2(b, a)
	wg.Wait()
	for x := range b {
		fmt.Println(x)
	}

}
本文内容由网友自发贡献,版权归原作者所有,本站不承担相应法律责任。如您发现有涉嫌抄袭侵权的内容,请联系:hwhale#tublm.com(使用前将#替换为@)

Golang协程常见面试题 的相关文章

  • 一份完整的软件测试报告

    基于软件发布测试编写一份完整的测试报告所包含的内容 xff1a 模板提取链接 xff1a https pan baidu com s 1l7opbNU7fwHXl9UacM4opQ 提取码 xff1a mgzo
  • 【STM32学习笔记】(10)——蜂鸣器实验详解

    蜂鸣器实验 蜂鸣器的简介 蜂鸣器是一种一体化结构的电子讯响器 xff0c 采用 直流电压供电 xff0c 广泛应用于计算机 打印机 复印机 报警器 电子玩具 汽车电子设备 电话机 定时器等电子产品中作发声器件 蜂鸣器主要分为 压电式蜂鸣器和
  • 计算机网络实验

    Ipconfig命令 1 实作一 Ipconfig命令通常被用户用来查看计算机中的ip地址 xff0c 子网掩码以及默认网关 与之类似的ipconfig all命令则是显示ipconfig中所有的详细信息 其中 xff0c ipconfig
  • 在linux系统下搭建鸿蒙bearPi的编译环境

    前言 xff1a 我是Linux使用的是Ubuntu18 04 进行环境配置的 xff0c 如果环境不同导致报错请自行网上搜索答案 1 首先先下载必要的插件 链接 xff1a https pan baidu com s 15E3SBXj g
  • keil玩儿51单片机时遇见的错误与警告

    一 错误1 错误展示 错误说明 xff1a 自己写的代码太大 xff0c 超过了keil5能够编辑的最大范围2048 xff1b 解决方法 xff1a 1 xff09 使用注册机 xff0c 破解keil5 xff1b 2 xff09 调整
  • Linux从入门到精通(Ubuntu 16.04)第一节实验

    第一节实验 命令操作 sudo su root 管理员身份 exit 退出管理员身份 xff0c 进入普通用户身份 shutdown 关机 reboot 重启 cd 进入根目录 cd 从当前目录后退一级 ls 查看当前目录文件及文件夹 cd
  • 计算机网络 第一节 基于 Windows 的 TCP/IP 实用程序:Ipconfig、Ping、Tracert、 Netstat、arp。

    了解基本的基于 Windows 的 TCP IP 实用程序 xff1a Ipconfig Ping Tracert Netstat arp 1 使用 ping 命令测试本地主机 TCP IP 的安装以及两台主机的连通情况 2 使用 ipco
  • IP地址,子网掩码,默认网关理解

    IP地址 xff0c 子网掩码 xff0c 默认网关 通俗来讲 xff1a IP 地址 xff0c 是标注一台电脑的身份 xff0c 如同每个人都有的身份证 xff1b 子网掩码表示所使用的网络属于哪种网络段 xff0c 两个IP地址同属于
  • Linux从入门到精通(Ubuntu 16.04)第二节实验

    1 重定向 cd home 在home文件夹 ls l gt test2 在home文件夹下新建了test2文件夹 xff0c 里面写入了home文件夹下的内容 2交换分区 注意 xff1a 要在cd root下进行 xff0c 当时在这里
  • Linux 从入门到精通(Ubuntu 16.04)第三节实验

    1 User 用户管理命令 xff08 1 xff09 adduser 添加用户 adduser a1 添加普通用户a1 adduser system home home a2 shell bin bash a2 添加系统用 xff0c 户
  • Labelme标签转COCO2017数据集格式

    以下代码是将Labelme标注软件标注的目标检测矩形框标签转换成COCO2017数据集格式进行训练 一 Labelme标注软件的安装 在Annaconda创建虚拟环境及安装Labelme conda create n labelme pyt
  • 深度剖析问题:Could not run ‘torchvision::nms‘ with arguments from the ‘CUDA‘ backend.

    问题 xff1a 使用YOLOv5进行测试的时候 xff0c 报错 xff1a Could not run 39 torchvision nms 39 with arguments from the 39 CUDA 39 backend x
  • 算法学习模板——素数问题、费马小定理、LCM/GCD和欧拉降幂

    万里之行 xff0c 始于足下 本博客总结近期学习到的部分数论模板 xff0c 以便于日后查询使用 作者水平有限 xff0c 难免存在疏漏不足 xff0c 恳请诸位看官斧正 倘若我的文章可以帮到你 xff0c 十分荣幸 当然 xff0c 以
  • NVIDIA显卡BUG解决 Unable to determine the device handle for GPU 0000:02:00.0: Unknown Error

    报错 实验室去年到今年断了几次电 xff0c 然后服务器上的2080Ti一直就感觉有点小毛病 属于是被折磨了几个月了 然后前两周断电后 xff0c 显卡就基本上完全用不了了 xff0c 经常服务器开机都会失败 并且就算服务器开机成功过后 x
  • 数据库E-R图基础概念

    E R图也称实体 联系图 Entity Relationship Diagram xff0c 提供了表示实体类型 属性和联系的方法 xff0c 用来描述现实世界的概念模型 ER模型的基本元素 实体 xff1a 用方框表示 xff0c 实体名
  • 51单片机蜂鸣器

    蜂鸣器分为两类 1 有源蜂鸣器 2 无源蜂鸣器 有源蜂鸣器比较简单 xff0c 只要有电流通过 xff0c 蜂鸣器就会发声 一般改变不了音调和音量 无源蜂鸣器要给一定频率的脉冲信号 xff0c 蜂鸣器才会发出声音 对于无源蜂鸣器只要改变频率
  • 数据分析----数据清洗

    文章目录 前言一 数据清洗是什么 xff1f 二 步骤1 选择列2 缺失值处理1 找到缺失值2 处理缺失值的方法 3 数据类型转化4 重复值处理 总结 前言 随着科技的不断发展 xff0c 数据在我们生活中越来越多 xff0c 面对繁杂的数
  • python装饰器

    装饰器 一 概念 1 装饰器 xff08 Decoration xff09 装饰器是一种设计模式 xff0c 经常用来实现 34 面向切面的编程 34 AOP 实现在不修改源代码的情况下 xff0c 给程序动态添加功能的一种技术 2 装饰器
  • 拒绝拖延!

  • 使用qemu-img转换镜像格式

    qemu功能强大 xff0c 详细了解其功能请到官网查看 https www qemu org docs master system images html qemu img能将RAW qcow2 VMDK VDI VHD xff08 vp

随机推荐

  • KEIL5MDK最新版(3.37)安装以及旧编译器(V5)安装

    最近KEIl5最新版本出来了 xff0c 但官方不在默认安装V5编译器 xff0c 导致某些代码无法兼容 xff0c 为了防止搞忘 xff0c 便把方法上传网上 旧编译器的安装思路是 在以前有V5编译器的KEILMDK安装包中复制粘贴到新的
  • mp4转ros bag包

    操作方法 python2 mp4 to bag py lane video mp4 lane camera bag 执行转化命令 rosbag play l TLout bag camera image raw 61 image raw0
  • 深度学习之卷积神经网络CNN详细

    需要PPT加Q1271370903 一 深度学习引入 1 各学习方法之间的联系 SL SSL和UL是传统ML方法 DL提供了一个更强大的预测模型 可产生良好的预测结果 RL提供了更口快的学习机制 且更适应环境的变化 TL突破了任务的限制 将
  • 基于51单片机实现红外循迹

    红外循迹外观 xff1a 红外循迹原理 xff1a 红外循迹模块原理还是很简单的 xff0c 和许多光电传感器原理一样 xff0c 当发射器发射出去的光被接收器接收到后 xff0c 模块上对应的LED灯点亮 xff0c 此时相应的输出引脚输
  • Ubuntu“从服务器获取共享列表失败:拒绝连接”问题的解决方法

    本来是可以的 xff0c 在安装nginx后 xff0c ubuntu连接不上共享文件 有过以下尝试 删除nginx和所有配置 xff0c 关闭代理 或者搜一些什么安装smbd包什么的 还有连接ip地址进行挂载的 以及开放端口 我解决的方法
  • C语言:scanf的使用

    目录 一 scanf的循环读取 1 scanf的一次读取 2 加入while循环使scanf能进行循环读取 3 由于scanf出错时会返回EOF xff0c 故代码改为 4 加入rewind清空缓冲区 xff08 只适用于vs xff09
  • 搭建mysql的主从关系

    目录 1 什么是mysql主从 2 为什么要使用MySQL主从 3 MySQL主从的实现原理 4 如何搭建mysql主从关系 4 1 搭建两台有mysql的虚拟机 可以克隆 4 2 保证自己的mysql可以远程访问 4 3 修改ip地址 因
  • 关于虚拟机装Anaconda教学

    文章目录 一 第一步装VMware Workstation Pro虚拟机的安装二 下载虚拟机镜像文件三 安装python解释器 xff0c 安装Anaconda xff1a python解释器 xff1a 直接在DOS上安装 xff0c 操
  • C#ftp服务器配置与文件上传

    个人分享 public void test ftp服务器路径 string ftpServer 61 34 ftp 192 168 0 1 34 ftp本地路径 string ftpDefaultUrl 61 34 A 34 登入到ftp的
  • 完整的前端项目开发流程

    一个项目从一开始的计划到最后的上线 大概要经过以下的流程 产品设计原型审评项目分工项目开发项目测试项目构建项目上线 1产品设计 主要负责人 产品经理 产品经理前期负责收集销售 客户 领导的零散需求 然后做需求分析 完成产品设计 需求分析 需
  • debian11 sid

    编辑 etc apt sources list xff0c 增加下面二行 xff1a deb http ftp us debian org debian sid main contrib non free deb src http ftp
  • 单片机学习——定时器/计数器

    单片机必学系列 单片机学习 中断系统 单片机学习 存储器详解 xff08 程序存储器 片内RAM 拓展RAM EEPROM xff09 单片机学习 定时器 计数器 单片机学习 A D转换 更新ing 单片机学习 定时器 计数器 单片机必学系
  • ros bag包转mp4视频

    先在 rosbag2video py 脚本中修改相关配置参数 或者在终端命令改也行 如果使用脚本里的默认参数 则可直接运行 python rosbag2video py 2022 02 02 10 41 38 bag 代码如下 span c
  • 零基础入门Jetson Nano——通过OpenCV调用CSI和USB摄像头

    Jetson Nano学习 摄像头调用 前言一 CSI摄像头的调用1 安装v4l2 utils协助工具2 查看摄像头挂载情况3 查看挂载的摄像头详细参数情况4 检测摄像头能否正常工作5 使用OpenCV调用CSI摄像头方式一 利用Gstre
  • HTML搜索框的制作过程

    1 搜索框的构成 input标签和button标签我的理解应该就类似于行内块元素 xff0c 可以设置宽高 xff0c 但同时又不独占一行placeholder 占位文本 就是输入框一开始默认显示的文本 span class token t
  • Linux

    Linux 文件管理创建目录创建文件复制剪切 重命名删除归档 xff08 解压 压缩 xff09 文件查看帮助文档查看信息文件显示当前工作目录切换目录请屏显示文件内容文件内容搜索文件搜索链接文件搜索命令所在位置 Linux系统查看进程终止进
  • isalpha()库函数

    C C 43 43 isalpha 库函数 isalpha 函数用来检测一个字符是否是字母 xff0c 包括大写字母和小写字母 头文件 xff1a 属于ctype h头文件 xff1b 但也包含在iostream头文件下 语法原型 xff1
  • 计算机网络(谢希仁-第八版)第四章习题全解

    4 01 网络层向上提供的服务有哪两种 xff1f 试比较其优缺点 xff1f 虚电路服务和数据报服务 虚电路 优点 xff1a 1 可以提供可靠的通信服务 2 因为数据是沿着建立的虚电路进行传输的 xff0c 因此分组的首部不需要携带完整
  • Linux下线程池(ThreadPool)

    目录 一 线程池相关概念及其优点 二 线程池的实现 一 线程池相关概念及其优点 线程池一种线程使用模式 xff0c 线程过多会带来这个调度的开销进而影响整体的性能 xff0c 而线程池是提前准备好了线程等待着管理者进行分配任务 1 线程池的
  • Golang协程常见面试题

    协程面试题 交替打印奇数和偶数N个协程打印1到maxVal交替打印字符和数字交替打印字符串三个协程打印ABCChannel练习 交替打印奇数和偶数 下面让我们一起来看看golang当中常见的算法面试题 使用两个goroutine交替打印1