gpt4 book ai didi

android - 如何在Android/iOS上通过不同的线程同时读写一个文件?

转载 作者:行者123 更新时间:2023-12-01 19:31:16 25 4
gpt4 key购买 nike

我有很多小文件。为了节省文件句柄并提高IO效率,这些文件打包成一个大文件。但是,由于某些原因,这些小文件应该能够在运行时更新。因此,需要通过不同的线程同时更新和读取大单个文件的不同部分。
由于存在内存限制,因此mmap不是一个好的选择。我必须自己实施。但是我担心在iOS / Android上同时读取和写入单个文件的不同部分是否安全。我如何确保正在写入的块不会被其他线程读取。
我应该通过线程锁定来实现整个功能,还是有一些成熟的技术可以完成相同的工作?
顺便说一句,我为项目使用C++。但是Java&Obj-C也是一种选择。
用户案例示例:
我的项目是RPG游戏。当人们看到未存储在原始包装中的物品时,游戏将从服务器加载该物品并将其立即自动保存到磁盘中。
一个项目对应一个文件。每个文件将近300KB〜1.5MB。服务器上有3000〜5000个项目。在最坏的情况下,人们将在本地保存数千个文件。
好消息是我的用户可以按需加载项目以节省存储空间。并且在更新时,将仅重新下载已更改的项目。但是成千上万个文件将导致用完FD或其他资源的高风险。
这就是为什么我想将这些小文件打包到一个大文件包中。但是我仍然想保持更新/添加单个文件的能力。

最佳答案

简而言之,是,锁仍然是处理此问题的最佳方法,并且将永远成为开发人员工具栏中的重要内容。
这种问题与解决问题的方法一样普遍,几乎使该答案意见成为基础。我会四处散布我的意见,但是您将需要根据对您最有利或更容易的选择做出自己的决定。
首先,在我看来,管理可变大小的大文件,其中包含许多可变大小的小东西,并使用多个线程即时删除和创建,就像设计和实现文件系统一样复杂。而且,与以下方法相比,我认为没有优势-嗯,也许它将很快发展起来。但是请相信我,您既不需要也不想走那条路。
因此,我不会完全回答您的原始问题,相反,我想向您展示解决问题的风险较小的方法。
出于实际目的,我将游戏项目称为asset。另外,我还要假设这些assets不是要供GPU直接使用,例如纹理,这可能需要我没有经验的新鲜知识。
=========
1-网络缓存方法

  • 找到一个缓存网络请求的库。
  • 每当您需要资产时,就假装您是从网络上获取资产的,它会为您提供二进制文件。如果是第一次,它将向服务器询问,否则很可能会在库缓存中找到副本。

  • ups:设置非常简单快捷。配置缓存大小,并根据LRU(最近使用的最少)清除旧对象。如果服务器设置正确,则您的应用程序会知道它是否具有资产的最新版本,或者是否有新版本要下载。无需担心锁和线程安全。
    downs:如果您错误地设置了缓存策略,并且服务器没有正确公开缓存头,那么效率可能非常低。
    对于这种方法,我建议使用Kotlin编写的Okhttp版本4。这意味着您可以在Android或iOS上运行它,并且应该相对容易地从C / C++ / Obj-C进行接口(尽管我还没有亲自尝试过),而在Java中却微不足道。
    当然,周围还有其他库,但是我不知道可以在C和Java / JVM中使用的其他库。
    =========
    2-分别跟踪各个 assets您可能需要中心类来确定资产是否可用,不可用或正在下载。您最终需要它来检查较新的版本,并最终删除其中的几个以节省空间。
    每个 asset要记住很多信息。我觉得自然的做法是拥有一个数据库来跟踪这种状态。
    现在您有2个选择。您可以将 asset作为Blob存储在数据库中。或获取唯一的文件名,将其自己保存到磁盘并将文件名存储在数据库中。我强烈建议使用后者,这将使您的调试更加容易,并且风险也较小。
    或者,您可以创建在应用启动时创建的类,扫描可用文件和版本,并将所有信息保存在内存中。
    ups:将每个 asset分别存储为磁盘上的文件或blob。您可以跟踪使用了多少次,并可以根据需要提出删除策略。
    起伏:选择数据库可能需要很长时间。特别是,SQLite和RealmDb可以在android和iOS中工作,因此您可以共享一些东西。
    在阅读此答案时,我发现这篇非常有趣的文章声称,在某些操作系统(包括Android)上,从sqlite读取存储的小blob(10kB)比从磁盘读取要快。有趣的惊喜,但只是略快一些,所以不值得为此而做。由于并行读取多个blob可能会在db上造成瓶颈。
    https://www.sqlite.org/fasterthanfs.html
    您只需要从磁盘读取 assets一样多的文件描述符即可。之后,您应该将其保留在内存中并关闭fd?
    ===============
    3-网络缓存,但具有内存缓存
    因此,这是在(1)之上的优化,以防万一变得太慢。但是,与所有性能优化一样,我强烈建议您在花时间之前进行评估。因此,最后您知道保存了多少时间,完成后是否值得进行额外的维护,而忘记了它是如何工作的。
    在这里,您可以汇总一个可以在内存中保存50个 assets的类,以便快速访问。如果没有 asset,它将要求网络库。
    ups:比(1)更具性能,比(2)复杂。
    起伏:它仍然比(1)更复杂。
    ================
    1001-大文件和mmap
    为什么将此选项编号为1001?因为按照我的建议顺序排列,所以我真的不推荐这种方法。
    我在多年前就使用过mmap,因此希望我能正确记住它的详细信息。充其量,它们仅适用于我使用过1核处理器的linux,请确认您在所需的平台上具有相同的行为。
    如果您创建一个1GB的文件并进行映射,则不会消耗1GB的RAM,因为那只是虚拟内存。当您读/写文件时,它确实消耗与页面错误导致的页面数量成比例的物理内存。
    您不需要任何锁即可读取或写入mmaped文件。只需对其进行读写,您就可以将下一个读取镜像到最后一个写入。现在,我早在2004年就在旧的1核心cpu计算机上完成了此操作。它们在现代多核CPU中的表现如何,如何确保在核1写入内存位置(也就是文件区域)之后,您可以在核2上读取相同的值,而不是先前写入的值?我不知道,并敦促您不要先学习它就不要实施它。
    您将需要为每个 offset分配 asset的算法的锁/信号量和线程安全性。当游戏要求输入 asset时,您需要确定它是否在磁盘上,这也意味着您知道它在磁盘上的位置。我们将此称为“where” offset。如果不是,则需要决定将其存储在哪里,下载它,并将文件 offset存储在某个地方。那就是您的代码中容易出现竞争条件的地方。
    ups:快。但不能确定比以前的方法快多少。如果您是第一次需要资产,您仍然需要等待页面错误,页面错误将从磁盘读取该文件区域并将其加载到物理内存中。
    失败:管理内存偏移量和跨内核同步页面错误将使您成为一个更好的程序员,但要花费很多时间和精力。根据我的经验,我很确定在ios或Android上会发生一些奇怪的事情,其行为与预期的不一样。像 Why does mmap fail on iOS?
    https://medium.com/i0exception/memory-mapped-files-5e083e653b1
    =================
    1002-大文件和lseek
    是的,还有一种我不推荐的方法。基本上就是上面的内容,但是不是使用mmap进行读写,而是为同一文件创建一个或多个文件描述符,然后使用 lseek读取/写入内存区域。
    它具有与先前选项相同的所有缺点,至多也具有相同的优点。

    关于android - 如何在Android/iOS上通过不同的线程同时读写一个文件?,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/62673342/

    25 4 0
    Copyright 2021 - 2024 cfsdn All Rights Reserved 蜀ICP备2022000587号
    广告合作:1813099741@qq.com 6ren.com