首页 文章

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

提问于
浏览
2

可能不清楚问题本身我在这之后是什么,所以让我澄清一下 . 作为并发练习,我正在编写一个需要多个同时请求可访问的缓存 . 缓存内容的类型为interface {},因此它可以包含任何内容,包括切片,贴图和结构 . 当我使用Get方法获取某些东西时,我在读取它时对其进行RLock,然后返回内容并使用延迟的RUnlock完成 .

这适用于数字和字符串以及返回时自动复制的任何其他值 . 但是我担心切片,贴图和结构实际上并没有被复制,因此返回的东西,如果被读取或修改就好像它是一个副本,实际上会改变缓存中的数据并在互斥体之外这样做 .

当然,这在竞争条件下是个问题 . 所以我不想从Get中返回一些不安全的东西,然后传回Set方法进行更新 . 以下是问题:

1)假设这些数据类型为这样的场景带来问题,我是否正确?

2)如何解决这个问题,以便创建一个Get方法,其值可以自由操作而不用担心在竞争条件下失败?

2 回答

  • 0

    假设这些类型的数据类型主要是引用并指向结构的指针可能导致问题是正确的,我将在下面讨论 .

    我看到你正在处理的两个问题 . 首先,您需要保护缓存不受并发访问的影响,以使缓存始终处于正确状态 . 如果要改变缓存并使用"write"锁定,则在以某种方式更改缓存时,缓存将保持其完整性 . 此外,只要您从缓存中读取"read"锁定,就可以保证以相同的完整性读取缓存 . 因此,锁定保护您的缓存 only work at protecting the cache itself . 这些锁不会做任何事情来保护缓存中存储的项目 .

    这是您要处理的第二个问题:假设您的缓存受到保护,请考虑如果两个单独的goroutine从缓存执行正确同步的Get操作会发生什么 . 它们甚至不一定要同时获取对象,但如果它们以某种方式结束"getting"指向某个结构或指向 Map /切片的指针,这意味着它们可能是 both mutate the same object ,它们都持有对它的引用 . 这表明您正在描述的第二个问题 .

    那你有什么选择?

    • 只有您观察到的存储值类型可能会限制和/或昂贵,因为必须复制所有内容 .

    • 仅存储一些同步的自定义类型,同时确保它们在变异或读取时对自己采取适当的锁定 .

    • 使您的缓存更智能,以便它具有所有权的概念,它将很乐意从缓存中返回一个对象,并且只允许一个goroutine到"hold",直到该goroutine完成 . 其他goroutine必须等待该对象被释放,直到上一个goroutine完成它 . 要么是这样,要么你可以设计Get to fail并在尝试获取当前不可用的项目时立即返回 . 此概念广泛用于在客户端服务器体系结构中构建分布式锁,其中可能有许多客户端需要访问对象,而分布式锁定确保只有一个客户端可以持有锁 .

    考虑所有权的概念很重要 . 有人可能会说:只使用渠道,这将解决所有问题 . 但是,如果您将引用类型或指向结构的指针发送到5个不同的通道,您甚至可以在同一条船上结束 . 这5个不同的通道可能会改变它们所持有的相同物体 . 哦哦!同样的问题再次表现出来 . 这就是为什么重要的是当你将一个项目传递给一个 Channels 时,你放弃了所有权而不改变它 .

    正如有人今天告诉我的那样... concurrent programming is hard 并且你可以尝试其他模式,但我希望这能让你更深入地了解你正在处理的问题 . 有一点需要知道的是,对此没有真正的防弹答案,其中很大一部分取决于应用程序最终行为的性质 .

  • 2

    1)假设这些数据类型为这样的场景带来问题,我是否正确?

    是 . 映射和切片具有指向内部数据结构的指针,这些数据结构不是't copied during assignment. But since you',你也可以使用带有指针的结构,这可能指向具有更多指针的结构,依此类推 .

    2)如何解决这个问题,以便创建一个Get方法,其值可以自由操作而不用担心在竞争条件下失败?

    最简单的解决方案是只允许一个对象一次只返回一个客户端,因此只有一个实时,可变的版本 . 您还可以序列化对象,以便所有内容始终都是副本 .

    任何需要同时写入和读取的内容都需要进行适当的同步 . 期 .

相关问题