当前位置: 首页 > 面试题库 >

使用引用同时处理结构片

颛孙品
2023-03-14
问题内容

我有一个JSON,我需要对其进行一些处理。它使用我需要以某种方式引用的切片,以便在函数末尾修改Room-struct。如何通过按引用类型同时使用此结构?

http://play.golang.org/p/wRhd1sDqtb

type Window struct {
    Height int64 `json:"Height"`
    Width  int64 `json:"Width"`
}
type Room struct {
    Windows []Window `json:"Windows"`
}

func main() {
    js := []byte(`{"Windows":[{"Height":10,"Width":20},{"Height":10,"Width":20}]}`)
    fmt.Printf("Should have 2 windows: %v\n", string(js))
    var room Room
    _ = json.Unmarshal(js, &room)

    var wg sync.WaitGroup
    // Add many windows to room
    for i := 0; i < 10; i++ {
        wg.Add(1)
        go func() {
            defer wg.Done()
            addWindow(room.Windows)
        }()
    }
    wg.Wait()

    js, _ = json.Marshal(room)
    fmt.Printf("Sould have 12 windows: %v\n", string(js))
}

func addWindow(windows []Window) {
    window := Window{1, 1}
    // Do some expensive calculations
    fmt.Printf("Adding %v to %v\n", window, windows)
    windows = append(windows, window)
}

问题答案:

您的逻辑中有两个不同的问题:第一个是切片本身的操作方式,第二个是实际的并发问题。

对于分片操作,仅按值传递分片作为参数将意味着您将无法以必须在增加分片或重新分配支持数组的情况下调用站点看到分片的方式进行分片容纳您要添加的新数据。有两种常见的处理方法。

通过返回新的切片:

func addWindow(windows []Window) []Window {
    return append(windows, Window{1, 1})
}

room.Windows = addWindow(room.Windows)

或者通过提供一个可变参数,呼叫站点可以维护对以下内容的引用:

func addWindow(room *Room) {
    room.Windows = append(room.Windows, Window{1, 1})
}

对于第二个问题,必须确保不会以不安全的方式同时对值进行突变。也有许多解决方法:

使用频道

您可以要求N个goroutine制作窗户,而不是直接操纵房间,并将其结果报告给非民主控制点。例如,您可能有:

windows := make(chan Window, N)
for i := 0; i < N; i++ { 
    go createWindow(windows)
}
for i := 0; i < N; i++ {
    room.Windows = append(room.Windows, <-windows)
}

并且addWindow反而会类似于:

func createWindow(windows chan Window) {
    windows <- Window{1, 1}
}

这样,创建是并发的,但是对房间的实际操作不是。

添加互斥锁字段

通常在类型本身中具有一个私有互斥字段,例如:

type Room struct {
    m       sync.Mutex
    Windows []Window
}

然后,每当操作对并发敏感的字段时,请使用互斥对象保护独占区域:

room.m.Lock()
room.Windows = append(room.Windows, window)
room.m.Unlock()

理想情况下,使用这种互斥锁应使其封装在靠近类型本身的地方,因此很容易发现其用法。因此,您经常会在类型本身的方法(room.addWindow例如,)中使用互斥量。

如果在专用(受保护)区域中有紧急情况发生的逻辑,则最好在Unlock呼叫之后立即推迟呼叫Lock。很多人甚至在简单的操作中都将一个接一个地放在另一个位置,只是这样,他们不必弄清楚这样做是否安全。如果不确定,那可能是个好主意。

非常重要:
在大多数情况下,按值复制带有互斥量字段的结构是个坏主意。而是使用指向原始值的指针。这样做的原因是,互斥锁在内部依赖于其字段的地址而不会更改,原子操作才能正常工作。

添加全局互斥锁

在更特殊的情况下,这很可能不适用于您尝试处理的情况,但是这是一个很好的了解,您可以选择保护逻辑本身而不是保护数据。一种实现方法是使用全局互斥变量,其中包含以下内容:

var addWindowMutex sync.Mutex

func addWindow(room *Room) {
    addWindowMutex.Lock()
    room.Windows = append(room.Windows, Window{1, 1})
    addWindowMutex.Unlock()
}

这样addWindow,无论谁调用它,它本身都受到保护。这种方法的优点是您不必依赖于实现的空间。缺点是,无论并行处理多少个房间,只有一个goroutine都会进入互斥区域(以前的解决方案不是这种情况)。

在执行此操作时,请记住,还应保护 读取 room.Windows或在独占区域中进行任何数据更改的任何数据,以防万一仍在进行并发更改。

最后,就像一些无提示的反馈一样,请检查那些错误值。不管是示例还是严肃的代码,忽略错误都是非常糟糕的做法。即使构建这样的示例代码,很多时候您也会捕获错误。



 类似资料:
  • 问题内容: 这是我无法完成的考试问题。 如何通过仅在MyClass构造函数中编辑代码来获取以下Java代码以打印false? 不允许您覆盖equals方法或更改main方法中的任何代码。该代码必须在程序不崩溃的情况下运行。 根据我的研究,实例化类时不能将Java对象引用设置为null。所以我正式陷入了困境。 问题答案: 太难了! 或Paul Boddington的简化版本: 或是AJ Neufel

  • 问题内容: 究竟是通过对吗? 我的应用程序处理了来自用户的输入,并且花了一些时间。在这段时间内,应用程序无法处理其他请求。我已经测试了我的应用程序,它可以让我同时处理多个请求。 问题答案: 从Flask 1.0开始,Flask随附的WSGI服务器默认在线程模式下运行。 在1.0之前的版本中,或者如果你禁用线程,则服务器以单线程模式运行,并且一次只能处理一个请求。任何并行请求都必须等待,直到可以处理

  • 问题内容: 是否可以为Alamofire请求添加超时处理程序? 在我的项目中,我以这种方式使用Alamofire: 编辑: 请求失败消息 错误域= NSURLErrorDomain代码= -1001“请求超时。” UserInfo = {NSUnderlyingError = 0x7fc10b937320 {Error Domain = kCFErrorDomainCFNetwork Code =

  • 是否可以为Alamofire请求添加超时处理程序? 在我的项目中,我使用Alamofire的方式如下: 编辑: 请求失败消息 Error Domain=NSURLErrorDomain Code=-1001“请求超时。”UserInfo={NSUnderlyingError=0x7fc10b937320{Error Domain=kCFErrorDomainCFNetwork Code=-1001

  • 问题内容: 免责声明:这是我第一次尝试该模块。 我使用以下方式尝试支持超时功能,以等待一组异步任务的所有结果。这是更大的库的一部分,因此我省略了一些不相关的代码。 请注意,该库已经支持通过ThreadPoolExecutors和ProcessPoolExecutors提交任务和使用超时,因此,我对使用这些代替建议或关于为什么要使用的问题并不真正感兴趣。转到代码… 起初,我不必担心在超时时取消待处理

  • 你能解释一下,当作为一个单独的进程执行辅助函数时,我是如何防止python GUI冻结的吗? 我编写了一个python GUI,点击一个按钮,就可以通过多处理模块启动一个进程。我决定使用多处理而不是线程,因为我喜欢选择启动、暂停、恢复和终止进程。 不幸的是,当辅助进程运行时,GUI会冻结并失去响应,因此我无法按下“暂停”按钮。 图形用户界面的冻结问题在stackoverflow上报告过几次,但是这