duanlou2917 2014-10-31 19:11
浏览 86
已采纳

在Go中,从并发可访问结构中检索到哪些数据类型后可以安全地进行读写?

Probably not clear from the question itself what I'm after here, so let me clarify. As an exercise in concurrency, I'm playing around with writing a cache that needs to be accessible by multiple simultaneous requests. Cache content is of type interface{}, so it can include anything, including slices, maps, and structs. When I grab something with a Get method, I RLock on it while reading it and then return the content and finish with a deferred RUnlock.

This works fine for numbers and strings and any other values that are automatically copied on return. But I'm concerned that slices, maps, and structs are not actually copied, such that the thing returned, if read or modified as if it were a copy, would actually be altering data in the cache and doing so outside of a mutex.

Of course, that's a problem under race conditions. So I don't want to return something from Get that is not safe to alter and then pass back to a Set method to update. So here are the questions:

1) Am I correct in assuming that these data types present problems for a scenario like this?

2) How might one go about resolving this issue, so as to create a Get method whose values can be freely manipulated without fear of failure under race conditions?

  • 写回答

2条回答 默认 最新

  • douhuiyan2772 2014-11-01 04:00
    关注

    You are correct in assuming those kinds of data types mainly references and pointers to structs can cause a problem for the reasons I'll talk about below.

    I see really two problems that are you dealing with. The first, is that you need to protect your cache from concurrent access such that the cache is always in a correct state. If you are mutating the cache and using a "write" lock, your cache will maintain its integrity when it is being changed in some way. Also, as long as you take a "read" lock when reading from the cache you are guaranteed to read out of your cache with that same integrity. So as it stands, the locks protecting your cache only work at protecting the cache itself. These locks will not do anything to protect the items stored within the cache.

    This is the second issue you are dealing with: Assuming your cache is protected, think about what would happen if two separate goroutines do a properly synchronized Get operation from your cache. They don't necessarily even have to Get the object at the same time but if somehow they end up "getting" a pointer to some struct or a reference to a map/slice this means they could potentially both mutate the same object of which they are both holding references to. This manifests itself as the second problem you are describing.

    So what are your options?

    1. Only store value types which as you have observed can be limiting and/or expensive because everything must be copied.
    2. Only store some custom types that are synchronized by also making sure they take appropriate locks on themselves when they are mutated or read.
    3. Make your cache smarter so that it has the concept of ownership where it will gladly return an object from the cache and only ever allow one goroutine to "hold" onto it until that goroutine is finished. Other goroutines would have to wait for that object to be released until the previous goroutine has finished with it. Either that, or you could design the Get to fail and return immediately if it tried to Get an item that was currently unavailable. This concept is widely used to build distributed locks in a client server architecture where there could be many clients that want access to an object and the distributed lock ensures that only one client can ever hold the lock.

    Consider that the concept of ownership is important. Someone might say: just use channels, that will fix everything. But you can even end up in the same boat if you send a reference type or a pointer to a struct into 5 different channels. Those 5 different channels could possibly mutate the same object they are holding onto. Uh oh! the same problem manifests itself again. This is why it's important that when you pass an item to a channel you are giving up ownership to not mutate it.

    As someone told me today...concurrent programming is hard and there are probably additional patterns out there you could try but I hope this gives some more insight into the problem you are dealing with. One thing to know is there isn't really a bullet-proof answer to this, much of it will depend on the nature of how your application behaves ultimately.

    本回答被题主选为最佳回答 , 对您是否有帮助呢?
    评论
查看更多回答(1条)

报告相同问题?

悬赏问题

  • ¥50 如何用脚本实现输入法的热键设置
  • ¥20 我想使用一些网络协议或者部分协议也行,主要想实现类似于traceroute的一定步长内的路由拓扑功能
  • ¥30 深度学习,前后端连接
  • ¥15 孟德尔随机化结果不一致
  • ¥15 apm2.8飞控罗盘bad health,加速度计校准失败
  • ¥15 求解O-S方程的特征值问题给出边界层布拉休斯平行流的中性曲线
  • ¥15 谁有desed数据集呀
  • ¥20 手写数字识别运行c仿真时,程序报错错误代码sim211-100
  • ¥15 关于#hadoop#的问题
  • ¥15 (标签-Python|关键词-socket)