【问题标题】:two way communication through channels in golanggolang中通过渠道进行双向通信
【发布时间】:2017-11-09 11:05:12
【问题描述】:

我有几个函数希望它们以原子方式执行,因为它们处理敏感数据结构。假设以下场景: 有两个函数:lock(sth)unlock(sth),goroutine 可以随时调用它们来锁定或解锁全局数组中的 sth。我正在考虑有一个命令通道,以便 goroutine 将 lockunlock 命令发送到通道中,并且在通道的接收端,某种 handler 处理 lockunlock 请求,顺序,通过从频道抓取命令。这很好,但是如果handler 想要将结果发送回请求者呢?是否可以使用 golang 频道这样做?我知道可以使用某种锁机制,例如互斥锁,但我想知道是否可以将通道用于此类用例?我在某处看到建议使用通道而不是 goland 低级锁结构。

一句话:

在容量为 1 的通道中,我希望接收方能够回复发送消息的 goroutine。

或等效:

一个 goroutine 发送一些东西到一个通道;消息被另一个 goroutine 接收并处理导致一些结果;发件人如何知道结果?

【问题讨论】:

    标签: multithreading go concurrency locking


    【解决方案1】:

    其他问题已经很好地涵盖了锁定,但我想解决关于使用通道将响应发送回调用者的问题的另一部分。在 Go 中有一种不常见的模式,即发送带有请求的响应通道。例如,您可以通过通道向处理程序发送命令;这些命令将是具有特定于实现细节的struct,并且该结构将包括用于将结果发送回的通道,键入结果类型。发送的每个命令都将包含一个新通道,处理程序将使用该通道发回响应,然后关闭。举例说明:

    type Command struct {
        // command parameters etc
        Results chan Result
    }
    
    type Result struct {
        // Whatever a result is in this case
    }
    
    var workQueue = make(chan Command)
    
    // Example for executing synchronously
    func Example(param1 string, param2 int) Result {
        workQueue <- Command{
            Param1: param1,
            Param2: param2,
            Results: make(chan Result),
        }
        return <- Results
    

    【讨论】:

      【解决方案2】:

      如果您想防止竞争条件,那么sync 原语应该可以正常工作,如@Nevermore 的回答中所述。它使代码更具可读性和更易于推理。

      但是,如果您希望频道为您执行同步,您可以随时尝试以下操作:

      // A global, shared channel used as a lock. Capacity of 1 allows for only
      // one thread to access the protected resource at a time.
      var lock = make(chan struct{}, 1)
      
      
      // Operate performs the access/modification on the protected resource.
      func Operate(f func() error) error {
          lock <- struct{}{}
          defer func() { <- lock }()
          return f()
      }
      

      要使用这个Operate,请传入一个访问受保护资源的闭包。

      // Some value that requires concurrent access.
      var arr = []int{1, 2, 3, 4, 5}
      
      // Used to sync up goroutines.
      var wg sync.WaitGroup
      wg.Add(len(arr))
      
      for i := 0; i < len(arr); i++ {
          go func(j int) {
              defer wg.Done()
      
              // Access to arr remains protected.
              Operate(func () error {
                  arr[j] *= 2
                  return nil
              })
      
          }(i)
      }
      wg.Wait()
      

      工作示例:https://play.golang.org/p/Drh-yJDVNh

      或者您可以完全绕过Operate,直接使用lock 以获得更高的可读性:

      go func(j int) {
          defer wg.Done()
      
          lock <- struct{}{}
          defer func() { <- lock }()
      
          arr[j] *= 2
      }(i)
      

      工作示例:https://play.golang.org/p/me3K6aIoR7

      如您所见,arr 访问受到此处使用通道的保护。

      【讨论】:

        【解决方案3】:

        sync 包包含一个互斥锁sync.Mutex,可以以线程安全的方式从任何 goroutine 锁定和解锁。与其使用通道发送命令来锁定某物,不如只使用来自发送者的互斥锁?

        mutex := new(sync.Mutex)
        sensitiveData := make([]string, 0)
        // when someone wants to operate on a sensitiveData,
        // ...
        mutex.Lock()
        operate(sensitiveData)
        mutex.Unlock()
        

        当您说发送者如何知道结果时,我认为您是在谈论处理程序如何接收结果 - 那将是 chan。您可以通过渠道发送数据。

        或者,如果您只想了解信号量,sync.WaitGroup 可能会完成这项工作。这个结构可以Add()ed 到,然后发送方可以wg.Wait() 直到处理程序调用wg.Done(),这将向发送方(正在等待)指示处理程序已经完成了这样的操作。


        如果您的问题是关于是否使用锁或通道,wiki 有一个简洁的答案:

        一个常见的 Go 新手错误是过度使用通道和 goroutine 只是因为它是可能的,和/或因为它很有趣。如果最适合您的问题,请不要害怕使用 sync.Mutex。 Go 在让您使用最能解决您的问题的工具而不是强迫您使用一种代码风格方面是务实的。

        不过,作为一般指南:

        频道:传递数据所有权、分配工作单元、传达异步结果
        互斥体:缓存、状态


        如果您绝对想避免除chans 之外的任何事情 :),请尽量不要更改敏感数组。相反,使用通道将数据发送到不同的 goroutine,在每一步处理数据,然后将处理后的数据汇集到最终类型的 goroutine 中。也就是说,完全避免使用数组并将数据存储在chans 中。

        正如座右铭所言,

        不要通过共享内存进行通信;相反,通过通信共享内存。

        【讨论】:

        • 非常感谢您的准确回答! :-) 这很有帮助,我承认最简单的方法是使用Mutex。但是,根据我有限的经验,如果可能的话,使用消息传递处理并发问题比使用共享内存解决方案要容易得多。我想知道是否可以在不使用信号量或互斥锁的情况下解决问题,而只需使用 golang 通道,现在我发现使用 Mutex 完全没问题。
        • 嗨@shidsun,我不确定我是否理解你的问题:),也许一些伪代码会有所帮助?无论如何,我的建议是完全避免使用数组(如果您只想使用通道)并尝试将数据存储在 chans 中。
        猜你喜欢
        • 1970-01-01
        • 2018-04-26
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 2011-05-07
        相关资源
        最近更新 更多