以惯用的方式创建带有额外标志的通道

huangapple go评论81阅读模式
英文:

Create channels with extra flags in an idiomatic way

问题

TL;DR 我想要实现这样的功能:一个通道有两个额外的字段,告诉生产者是否允许向通道发送消息,并且如果允许,则告诉生产者消费者期望的值。虽然我知道可以使用共享内存来实现,但我认为这种方法违背了Go的理念:“不要通过共享内存来通信,而是通过通信来共享内存”。

背景:

我希望有一个服务器S,除其他功能外,还运行着三个goroutine:

  1. 监听器只接收UDP数据包并将其发送到多路复用器。
  2. 多路复用器根据一些数据将网络数据包发送到多个通道中的一个。
  3. 处理任务监听一个特定的通道,并处理在该通道上接收到的数据。

为了检查网络上的某些设备是否仍然存活,处理任务将定期通过网络发送随机数,并等待k秒钟。在这k秒钟内,协议的其他参与者将收到该随机数,并发送一个包含(除其他信息外)该随机数的回复。多路复用器将从监听器接收数据包,解析它们,并将它们发送到“processing_channel”。在经过k秒后,处理任务将处理多路复用器推送到“processing_channel”的消息。

我希望多路复用器不仅仅是盲目地将接收到的任何正确类型的响应发送到“processing_channel”,而是要检查处理任务当前是否期望任何消息,如果是,则检查期望的随机数值是多少。我做出了这个设计决策,以便尽快丢弃不需要的数据包。

我的方法:

在其他语言中,我会有一个具有以下字段的类(伪代码):

class ActivatedChannel{
    boolean    flag_expecting_nonce;
    int        expected_nonce;
    LinkedList chan;
}

多路复用器在接收到正确类型的数据包时,会获取“ActivatedChannel processing_channel”对象的锁,检查标志是否设置以及随机数是否匹配,如果是,则将消息添加到“LinkedList chan”中。

问题:

这种方法使用了锁和共享内存,这与Go的“不要通过共享内存来通信,而是通过通信来共享内存”的理念不符。因此,我想知道:

  1. ...我的方法是否在Go中是“不好”的,因为它依赖于共享内存。
  2. ...如何以更符合Go风格的方式实现所述的结果。
英文:

TL;DR I want to have the functionality where a channel has two extra fields that tell the producer whether it is allowed to send to the channel and if so tell the producer what value the consumer expects. Although I know how to do it with shared memory, I believe that this approach goes against Go's ideology of "Do not communicate by sharing memory; instead, share memory by communicating."

Context:

I wish to have a server S that runs (besides others) three goroutines:

  1. Listener that just receives UDP packets and sends them to the demultplexer.
  2. Demultiplexer that takes network packets and based on some data sends it into one of several channels
  3. Processing task which listens to one specific channel and processes data received on that channel.

To check whether some devices on the network are still alive, the processing task will periodically send out nonces over the network and then wait for k seconds. In those k seconds, other participants of my protocol that received the nonce will send a reply containing (besides other information) the nonce. The demultiplexer will receive the packets from the listener, parse them and send them to the processing_channel. After the k seconds elapsed, the processing task processes the messages pushed onto the processing_channel by the demultiplexer.

I want the demultiplexer to not just blindly send any response (of the correct type) it received onto the the processing_channel, but to instead check whether the processing task is currently even expecting any messages and if so which nonce value it expects. I made this design decision in order to drop unwanted packets a soon as possible.

My approach:

In other languages, I would have a class with the following fields (in pseudocode):

class ActivatedChannel{
    boolean    flag_expecting_nonce;
    int        expected_nonce;
    LinkedList chan;
}

The demultiplexer would then upon receiving a packet of the correct type simply acquire the lock for the ActivatedChannel processing_channel object, check whether the flag is set and the nonce matches, and if so add the message to the LinkedList chan!

Problem:

This approach makes use of locks and shared memory, which does not align with Golang's "Do not communicate by sharing memory; instead, share memory by communicating" mantra. Hence, I would like to know... :

  1. ... whether my approach is "bad" regarding Go in the sense that it relies on shared memory.
  2. ... how to achieve the outlined result in a more Go-like way.

答案1

得分: 1

是的,你描述的方法与Golang的惯用实现方式不一致。你正确地指出,在上述方法中,你是通过共享内存进行通信的。

为了以Go的惯用方式实现这一点,其中一种方法是你的Demultiplexer“记住”所有期望接收nonce和相应nonce类型的processing_channels。每当一个processing_channel准备好接收回复时,它向Demultiplexe发送一个信号,表示它正在等待回复。

由于Demultiplexer是所有通信的中心,它可以维护一个processing_channel与其期望的nonce之间的映射。它还可以维护一个“注册表”,记录所有期望回复的processing_channels

在这种方法中,我们是通过“通过通信来共享内存”。

为了表示一个processing_channel正在等待回复,可以使用以下struct

type ChannelState struct {
    ChannelId        string // 用于标识处理通道的唯一标识符
	IsExpectingNonce bool
	ExpectedNonce    int
}

在这种方法中,没有使用锁。

英文:

Yes, the approach described by you doesn't align with Golang's Idiomatic way of implementation. And you have rightly pointed out that in the above approach you are communicating by sharing memory.

To achieve this in Go's Idiomatic way, one of the approaches could be that your Demultiplexer "remembers" all the processing_channels that are expecting nonce and the corresponding type of the nonce. Whenever a processing_channels is ready to receive a reply, it sends a signal to the Demultiplexe saying that it is expecting a reply.

Since Demultiplexer is at the center of all the communication it can maintain a mapping between a processing_channel & the corresponding nonce it expects. It can also maintain a "registry" of all the processing_channels which are expecting a reply.

In this approach, we are Sharing memory by communicating

For communicating that a processing_channel is expecting a reply, the following struct can be used:

type ChannelState struct {
    ChannelId        string // unique identifier for processing channel
	IsExpectingNonce bool
	ExpectedNonce    int
}

In this approach, there is no lock used.

huangapple
  • 本文由 发表于 2021年5月22日 18:23:54
  • 转载请务必保留本文链接:https://go.coder-hub.com/67648394.html
匿名

发表评论

匿名网友

:?: :razz: :sad: :evil: :!: :smile: :oops: :grin: :eek: :shock: :???: :cool: :lol: :mad: :twisted: :roll: :wink: :idea: :arrow: :neutral: :cry: :mrgreen:

确定