当前位置: 首页 > news >正文

Go语言现代web开发14 协程和管道

概述

Concurrency is a paradigm where different parts of the program can be executed in parallel without impact on the final result. Go programming supports several concurrency concepts related to concurrent execution and communication between concurrent executions.

并发是一种范例,其中程序的不同部分可以并行执行而不会影响最终结果。Go编程支持与并发执行和并发执行之间的通信相关的几个并发概念。

协程

A thread is a small sequence of instructions that can be processed by a single CPU core. Moder hardware architectures have multiple cores, so we can execute multiple threads in paralle.

线程是可以由单个CPU核心处理的一小段指令序列。现代硬件架构有多个内核,因此我们可以并行执行多个线程。

Go programming language offers its own solution for concurrent execution, called goroutines. Goroutines can be defined as lightweight threads (because they are very small, only a couple of KB will be used to store all thread-related data on the stack) managed by Go routine.

Go编程语言为并发执行提供了自己的解决方案,称为gooutines。Go例程可以定义为由Go例程管理的轻量级线程(因为它们非常小,只需要几个KB就可以将所有与线程相关的数据存储在堆栈上)。

If we use the go keyword in front of a function call, that function will be executed in a new goroutine, but the evaluation of arguments will be executed in the current goroutine. Here we have two function calls, the first one will be executed in the current goroutine and for the second one, a new goroutine will be created.

如果我们在函数调用前使用go关键字,那么该函数将在新的运行例程中执行,但参数的计算将在当前运行例程中执行。这里我们有两个函数调用,第一个将在当前的运行例程中执行,第二个将创建新的运行例程。

sendMessage(message1)
go sendMessage(message2)

Goroutines run in the same address space so, in certain situations, goroutines must synchronize memory access and communicate with each other.

运行在相同的地址空间中,因此,在某些情况下,运行例程必须同步内存访问并相互通信。

完整示例代码:

package mainimport ("fmt""time"
)func sendMessage(msg string) {fmt.Println(msg)
}func main() {sendMessage("hello, Mara!")// main goroutine 一旦执行结束,其他goroutine不再执行,被迫结束go sendMessage("Hi, Mara!")// 方法1:延长main的执行时间time.Sleep(time.Second)
}

管道

We can define the channel as a construct through which we can send and receive values. In order to send or receive value, we will use the <-(arraow) operator in the following way:

  • ch <- value will send value to channel ch
  • value = <- ch will receive value from channel ch

我们可以将管道定义为可以发送和接收值的构造。为了发送或接收值,我们将以以下方式使用<-(箭头)操作符:

  • ch <- value 将发送值到管道ch
  • value = <- ch 将从管道ch接收值

As can be seen, the data flow is determined by the direction of the arrow.

可以看出,数据流是由箭头方向决定的。

We have two types of channels:

  • Unbuffered without buffer for message storage
  • Buffered with buffer for message storage

我们有两种管道:

  • 不带缓存的管道,没有缓存存储消息
  • 带缓存的管道,通过缓存存储消息

By default, the channel is unbuffered with send and receive as blocking operations. The sender will be blocked until the receiver is ready to pick up the variable from the channel and vice versa. The receiver will be blocked, waiting for the value until the sender sends it to the channel. This can be very useful because there is no need for any additional synchronization.

默认情况下,管道是无缓冲的,发送和接收都是阻塞操作。发送方将被阻塞,直到接收方准备好从通道中获取变量,反之亦然。接收方将被阻塞,等待该值,直到发送方将其发送到通道。这非常有用,因为不需要任何额外的同步。

Traditionally, we use the make() function to create a channel. This will create a new channel that allows us to send and receive string variables.

传统上,我们使用make()函数来创建管道。下面的示例将创建一个允许我们发送和接收字符串变量的新管道。

ch := make(chan string)

Through the channels which we have dealt so far, we can send only one value. But in practice, this is not an acceptable solution, for example, if the sender is faster than the receiver, the sender will be blocked too often. We can avoid that by defining a buffer, now we can accept more variables. Channels with buffers are called buffered channels.

对于我们刚才创建的管道,我们只能发送一个值。但在实践中,这不是一个可接受的解决方案,例如,如果发送方比接收方快,发送方就会经常被阻塞。我们可以通过定义缓冲区来避免这种情况,现在我们可以接受更多的变量。具有缓冲区的通道称为缓冲通道。

A buffered channel will be created by adding buffer size as a second parameter in the make() function.

通过在make()函数中添加缓冲区大小作为第二个参数来创建缓冲通道。

ch = make(chan string, 100)

With the buffered channel, the sender will be blocked only when the buffer is full and the receiver will be blocked only when the buffer is full and the receiver will be blocked only when the buffer is empty. In the next code example, we will use the bufferedchannel to send two messages from new goroutines and receive those messages in the main goroutine.

对于缓冲通道,只有当缓冲区已满时发送方才会被阻塞,只有当缓冲区为空时接收方才会被阻塞。在下一个代码示例中,我们将使用bufferedchannel从新的运行例程发送两条消息,并在主运行例程中接收这些消息。

func sendMessage(message string, ch chan string) {ch <- message
}func main() {ch := make(chan string, 2)go sendMessage("Hello", ch)go sendMessage("World", ch)fmt.Println(<-ch)fmt.Println(<-ch)
}

We cannot influence which goroutine will send the message first, words Hello and World will not always be displayed in that order on standard output.

我们无法影响哪个程序将首先发送消息,单词Hello和World在标准输出中并不总是按该顺序显示。

The sender and only the sender can close the channel when there are no more values to send by calling the close() function.

当没有更多的值可以发送时,只有发送方可以通过调用close()函数关闭通道。

close(ch)

The receiver should check if the channel is closed. In the following expression, variable ok will have the value false when the channel is closed.

接收端应该检查信道是否关闭。在下面的表达式中,当通道关闭时,变量ok的值为false。

v ok <- ch

Constant checking an be tiresome, but luckily, a special kind of for range loop can be used. If we put the channel in for range, the loop will receive values until the channel is closed.

不断的检查是令人厌烦的,但幸运的是,一种特殊的范围循环可以使用。如果我们将通道设置为for range,循环将接收值,直到通道关闭。

for v := range ch {fmt.Println(v)
}

If we try to send a message to a closed channel, panic will be triggered. When panic occurs, a message will be displayed on standard output and the function where panic has occurred with crash.

如果我们试图向封闭通道发送信息,就会引发panic异常。当panic发生时,将在标准输出和发生panic的函数上显示一条消息并崩溃。

In all of our previous examples, the receiver waits on only one channel, but Go provides us with the concept that allows the receiver to wait on multiple channels: select statement. Syntatically, select statement is similar to be switch statement, with one differece, keyword select is used instead of keyword switch. The receiver will be blocked until one of the case statements can be executed.

在前面的所有示例中,接收方只在一个通道上等待,但是Go为我们提供了select语句,允许接收方在多个通道上等待。从语法上讲,select语句与switch语句类似,区别在于使用关键字select而不是关键字switch。接收方将被阻塞,直到其中一个case语句可以执行。

select{case <- ch1:fmt.PPrintln("Channel One")case <- ch2:fmt.Println("Channel Two")default:fmt.Println("Waiting")
}

A default case will be executed if no other case isready.

如果没有准备好其他情况,将执行默认情况。

相关文章:

  • 北京网站建设多少钱?
  • 辽宁网页制作哪家好_网站建设
  • 高端品牌网站建设_汉中网站制作
  • QT中各数据基础类型互转方式有哪些?
  • Docker:简化应用部署与管理的神奇容器
  • 【Kubernetes】常见面试题汇总(二十三)
  • AI音乐创作带给音乐原创人的挑战和机遇
  • 深入浅出Docker
  • unity 高性能对象池解决方案
  • Uniapp 跨域
  • 冒泡排序,选择排序,插入排序,归并排序,快速排序五种排序方法
  • JavaScript Window localStorage使用
  • 基于springboot的智慧社区微信小程序
  • Linux中使用cp命令的 -f 选项,但还是提醒覆盖的问题
  • 【Web】御网杯信息安全大赛2024 wp(全)
  • Python语法(二)——函数
  • vue3 组合式API defineEmits() 与 emits 组件选项
  • STM32 单片机最小系统全解析
  • [iOS]Core Data浅析一 -- 启用Core Data
  • [译] 理解数组在 PHP 内部的实现(给PHP开发者的PHP源码-第四部分)
  • bootstrap创建登录注册页面
  • Centos6.8 使用rpm安装mysql5.7
  • CSS魔法堂:Absolute Positioning就这个样
  • Github访问慢解决办法
  • Intervention/image 图片处理扩展包的安装和使用
  • Java读取Properties文件的六种方法
  • Java知识点总结(JavaIO-打印流)
  • Js实现点击查看全文(类似今日头条、知乎日报效果)
  • Spring Boot快速入门(一):Hello Spring Boot
  • Swoft 源码剖析 - 代码自动更新机制
  • 阿里云Kubernetes容器服务上体验Knative
  • 测试开发系类之接口自动化测试
  • 测试如何在敏捷团队中工作?
  • 简单基于spring的redis配置(单机和集群模式)
  • 前嗅ForeSpider采集配置界面介绍
  • 腾讯优测优分享 | 你是否体验过Android手机插入耳机后仍外放的尴尬?
  • 正则学习笔记
  • kubernetes资源对象--ingress
  • ​ArcGIS Pro 如何批量删除字段
  • # Redis 入门到精通(七)-- redis 删除策略
  • #NOIP 2014# day.1 T3 飞扬的小鸟 bird
  • #QT 笔记一
  • #数据结构 笔记一
  • (C++哈希表01)
  • (html转换)StringEscapeUtils类的转义与反转义方法
  • (M)unity2D敌人的创建、人物属性设置,遇敌掉血
  • (Matalb分类预测)GA-BP遗传算法优化BP神经网络的多维分类预测
  • (Mirage系列之二)VMware Horizon Mirage的经典用户用例及真实案例分析
  • (教学思路 C#之类三)方法参数类型(ref、out、parmas)
  • (贪心) LeetCode 45. 跳跃游戏 II
  • (转)ORM
  • (转载)虚函数剖析
  • (自用)网络编程
  • .net core docker部署教程和细节问题
  • .NET/C# 编译期能确定的字符串会在字符串暂存池中不会被 GC 垃圾回收掉
  • .NET开源的一个小而快并且功能强大的 Windows 动态桌面软件 - DreamScene2
  • @DateTimeFormat 和 @JsonFormat 注解详解
  • @javax.ws.rs Webservice注解