首页
学习
活动
专区
圈层
工具
发布
首页
学习
活动
专区
圈层
工具
社区首页 >问答首页 >从多个线程写入文件

从多个线程写入文件
EN

Stack Overflow用户
提问于 2012-02-16 22:11:26
回答 3查看 3.7K关注 0票数 7

我正在用Objective-C编写一个下载管理器,它可以同时从多个段下载文件,以提高速度。文件的每个段都是以线程的形式下载的。

一开始,我想将每个片段写在不同的文件中,并在下载结束时将所有文件放在一起。但由于许多原因,这不是一个好的解决方案。

因此,我正在寻找一种在特定位置写入文件的方法,这种方法能够处理多线程,因为在我的应用程序中,每个段都是在线程中下载的。在Java语言中,我知道FileChannel完美地完成了这项工作,但在Objective-C中我却一无所知。

EN

回答 3

Stack Overflow用户

回答已采纳

发布于 2012-02-17 01:04:00

永远不要忘记,Obj-C基于普通的C,因此我只需要编写一个自己的类,它使用标准C API处理文件I/O,它允许您将当前写入位置放在新文件中的任何位置,甚至远远超出当前文件的大小(缺少的字节由零字节填充),还可以随意向前和向后跳转。实现线程安全的最简单方法是使用锁,这不是最快的方法,但在您的特定情况下,我打赌瓶颈肯定不是线程同步。这个类可以有一个像这样的头:

代码语言:javascript
运行
AI代码解释
复制
@interface MultiThreadFileWriter : NSObject
{
    @private
        FILE * i_outputFile;
        NSLock * i_fileLock;
}
- (id)initWithOutputPath:(NSString *)aFilePath;
- (BOOL)writeBytes:(const void *)bytes ofLength:(size_t)length
    toFileOffset:(off_t)offset;
- (BOOL)writeData:(NSData *)data toFileOffset:(off_t)offset;
- (void)close;
@end

和一个类似于下面的实现:

代码语言:javascript
运行
AI代码解释
复制
#import "MultiThreadFileWriter.h"

@implementation MultiThreadFileWriter

- (id)initWithOutputPath:(NSString *)aFilePath
{
    self = [super init];
    if (self) {
        i_fileLock = [[NSLock alloc] init];
        i_outputFile = fopen([aFilePath UTF8String], "w");
        if (!i_outputFile || !i_fileLock) {
            [self release];
            self = nil;
        }
    }
    return self;
}

- (void)dealloc
{
    [self close];
    [i_fileLock release];
    [super dealloc];
}

- (BOOL)writeBytes:(const void *)bytes ofLength:(size_t)length
    toFileOffset:(off_t)offset
{
    BOOL success;

    [i_fileLock lock];
    success = i_outputFile != NULL
        && fseeko(i_outputFile, offset, SEEK_SET) == 0
        && fwrite(bytes, length, 1, i_outputFile) == 1;
    [i_fileLock unlock];
    return success;
}

- (BOOL)writeData:(NSData *)data toFileOffset:(off_t)offset
{
    return [self writeBytes:[data bytes] ofLength:[data length]
        toFileOffset:offset
    ];
}

- (void)close
{
    [i_fileLock lock];
    if (i_outputFile) {
        fclose(i_outputFile);
        i_outputFile = NULL;
    }
    [i_fileLock unlock];
}
@end

锁可以通过各种方式避免。使用中央分派和数据块来调度串行队列上的查找+写入操作将会起作用。另一种方法是使用UNIX ( POSIX )文件处理程序,而不是标准的C处理程序(open()int,而不是FILE *fopen()),多次复制处理程序(dup()函数),然后将每个处理程序放在不同的文件偏移量上,这避免了在每次写入和锁定时进一步寻找操作,因为POSIX I/O是线程安全的。然而,这两种实现都会稍微复杂一些,可移植性较差,并且没有可测量的速度改进。

票数 3
EN

Stack Overflow用户

发布于 2012-02-17 01:49:26

到目前为止,给出的答案有一些明显的缺点:

使用系统调用的

  • 文件i/o肯定有一些缺点,因为内存中的locking.
  • Caching部件会在内存受限的环境中导致严重的问题。(即任何计算机)

一种线程安全、高效、无锁的方法是使用内存映射,其工作原理如下:

为read/write

  • mmap()
  • 创建(至少)文件总长度的结果文件到内存中的某个位置。文件现在“存在”在内存中的接收部分中,在文件中的右偏移处跟踪所有的部分是否已经接收到(例如,通过在主线程上张贴一些选择器来获取接收到的每个部分,并对内存进行stored)
  • munmap()
  • close()

实际的编写是由内核处理的--您的程序永远不会发出任何形式的write系统调用。内存映射通常没有什么缺点,被广泛用于共享库之类的东西。

更新:一段代码可以说超过1000个单词...这是基于锁的多线程文件写入器的mmap版本。注意,写入被简化为一个简单的memcpy,它不会失败(!!),因此没有要检查的BOOL success。性能相当于基于锁的版本。(通过并行写入100个1mb块进行测试)

关于基于mmap的方法的"overkill“的评论:这种方法使用更少的代码行,不需要锁定,在编写时不太可能阻塞,在编写时不需要检查返回值。唯一的“过度杀伤力”是,它要求开发人员理解另一个概念,而不是好的旧的读/写文件I/O。

直接读入mmapped内存区域的可能性被省略了,但实现起来相当简单。您可以直接在文件中使用read(fd,i_filedata+offset,length);recv(socket,i_filedata+offset,length,flags);

代码语言:javascript
运行
AI代码解释
复制
@interface MultiThreadFileWriterMMap : NSObject
{
@private
    FILE * i_outputFile;
    NSUInteger i_length;
    unsigned char *i_filedata;
}

- (id)initWithOutputPath:(NSString *)aFilePath length:(NSUInteger)length;
- (void)writeBytes:(const void *)bytes ofLength:(size_t)length
      toFileOffset:(off_t)offset;
- (void)writeData:(NSData *)data toFileOffset:(off_t)offset;
- (void)close;
@end

#import "MultiThreadFileWriterMMap.h"
#import <sys/mman.h>
#import <sys/types.h>

@implementation MultiThreadFileWriterMMap

- (id)initWithOutputPath:(NSString *)aFilePath length:(NSUInteger)length
{
    self = [super init];
    if (self) {
        i_outputFile = fopen([aFilePath UTF8String], "w+");
        i_length = length;
        if ( i_outputFile ) {
            ftruncate(fileno(i_outputFile), i_length);
            i_filedata = mmap(NULL,i_length,PROT_WRITE,MAP_SHARED,fileno(i_outputFile),0);
            if ( i_filedata == MAP_FAILED ) perror("mmap");
        }
        if ( !i_outputFile || i_filedata==MAP_FAILED ) {
            [self release];
            self = nil;
        }
    }
    return self;
}

- (void)dealloc
{
    [self close];
    [super dealloc];
}

- (void)writeBytes:(const void *)bytes ofLength:(size_t)length
      toFileOffset:(off_t)offset
{
    memcpy(i_filedata+offset,bytes,length);
}

- (void)writeData:(NSData *)data toFileOffset:(off_t)offset
{
    memcpy(i_filedata+offset,[data bytes],[data length]);
}

- (void)close
{
    munmap(i_filedata,i_length);
    i_filedata = NULL;
    fclose(i_outputFile);
    i_outputFile = NULL;
}

@end
票数 10
EN

Stack Overflow用户

发布于 2012-02-16 22:47:27

将段对象排入队列,因为它们被接收到写线程。写线程应该保留一个无序对象的列表,以便实际的磁盘写入是顺序的。如果一个片段下载失败,它可以被推回到下载线程池中进行另一次尝试(也许应该保留一个内部重试计数)。我建议使用段对象池来防止一个段的一个或多个下载失败,从而在下载后段并将其添加到列表中时导致失控的内存使用。

票数 4
EN
页面原文内容由Stack Overflow提供。腾讯云小微IT领域专用引擎提供翻译支持
原文链接:

https://stackoverflow.com/questions/9319874

复制
相关文章
Write your own Excel in 100 lines of F#
I've been teaching F# for over seven years now, both in the public F# FastTrack course that we run at SkillsMatter in London and in various custom trainings for private companies. Every time I teach the F# FastTrack course, I modify the material in one way or another. I wrote about some of this interesting history last year in an fsharpWorks article. The course now has a stable half-day introduction to the language and a stable focus on the ideas behind functional-first programming, but there are always new examples and applications that illustrate this style of programming.
仇诺伊
2020/04/24
6650
Write your own Excel in 100 lines of F#
红队技巧之F#利用
F # 是一种函数编程语言,可方便编写正确且可维护的代码。F # 编程主要涉及如何定义自动推断和通用化的类型和函数。这样,你的关注点将保留在问题域上并操作其数据,而不是编程的详细信息。
鸿鹄实验室
2021/07/06
1.5K0
红队技巧之F#利用
指针值传递、地址传递和引用传递
执行结果中并未输出字符串hello其实这里主函数调用fun函数,形参向实参传递参数的时候,发生的是拷贝。在fun函数中对局部指针变量p的任何修改都不会影响到主函数中的指针变量p。 下面简单的用函数栈帧空间图分析一下:
lexingsen
2022/02/24
1.8K0
指针值传递、地址传递和引用传递
细说值传递、引用传递和地址传递
形式参数:是在定义函数名和函数体的时候使用的参数,目的是用来接收调用该函数时传入的参数。
闫同学
2023/10/14
2450
Java的参数传递是值传递还是引用传递
当一个对象被当作参数传递到一个方法后,在此方法内可以改变这个对象的属性,那么这里到底是值传递还是引用传递?    答:是按值传递。Java 语言的参数传递只有按值传递。当一个实例对象作为参数被传递到方
nnngu
2018/03/15
3.3K0
Java的参数传递是值传递还是引用传递
Java 参数传递是值传递还是引用传递?
首先把结论表明,Java 的参数传递是值传递,因为有部分细节让人引起误解以为是引用传递,故我们写两个例子来举例探讨。
萬物並作吾以觀復
2019/05/10
2.9K0
java中的参数传递-值传递、引用传递
参数是按值而不是按引用传递的说明 Java 应用程序有且仅有的一种参数传递机制,即按值传递。
chenchenchen
2022/11/29
4.7K0
java中的参数传递-值传递、引用传递
值传递与引用传递
“哥,说说 Java 到底是值传递还是引用传递吧?”三妹一脸的困惑,看得出来她被这个问题折磨得不轻。
沉默王二
2021/06/16
1.2K0
值传递与引用传递
参考
Sketch中的插件系统可让您完全访问应用程序的内部结构和macOS中的核心框架。所以你有一个巨大的力量来构建几乎任何东西。 然而,伟大的力量有很大的责任,所以你需要在每个Sketch版本中留意你的代码。我们会在重构时不时更改Sketch的内部结构,因此您的插件可能会调用一些已重命名或删除的方法。 我们确实意识到这当然不是理想的。这就是为什么我们支持内部和插件之间的JavaScript API。我们希望它覆盖了90%的用例。如果没有,您可以随时进入内部,风险自担。 下面的页面包含插件可以侦听的所有操作的简要
iOSDevLog
2018/05/17
7830
值传递和引用传递
  java应用程序有且只有一种参数传递机制,即按值传递。   按值传递:当将一个参数传递给一个函数的时候,函数接收的是原始值的一个副本,因此,如果函数修改了该参数,仅仅修改的是参数的副本,而原始值保持不变。按引用传递一位置当一个参数传递给一个函数的时候,函数接收的是原始值的内存地址,而不是值的副本。因此,如果函数修改了该参数,调用代码中的原始值也随之改变。   1.对象是按照引用传递;   2.java中仅存在一种参数传递机制,即按值传递;   3.按值传递意味着当一个参数传递给一个函数的时候,函数接收
Mister24
2018/05/14
1.6K0
dotnet 通过 Elmish.WPF 使用 F# 编写 WPF 应用
本文来安利大家一个有趣而且强大的库,通过 F# 和 C# 混合编程编写 WPF 应用,可以在 WPF 中使用到 F# 强大的数据处理能力
林德熙
2021/05/20
1.8K0
ONNX模型 NuGet指南 异步编程技巧 F#有未来?
内容 Using .NET Hardware Intrinsics API to accelerate machine learning scenarios 用硬件加速机器学习 Fantomas
杜金房
2020/12/21
6290
ONNX模型 NuGet指南 异步编程技巧 F#有未来?
按值传递还是引用传递?
改变u的指向不会影响user,但如果改变u指向实例的内容name,那么就会影响到user了
晚上没宵夜
2020/05/06
1.1K0
python中值传递还是引用传递?
首先,Python中一切事物皆对象,变量是对对象在内存中的存储和地址的抽象。所有的变量都可以理解是内存中一个对象的“引用”,或者,也可以看似c中void*的感觉。
Tim在路上
2020/08/04
1.3K0
Java 引用传递和值传递
这个问题的关键在于 a,b,x,y 的地址指向; y = x 与 b = a 是不等价的!! 发生改变的是 y 指向的值变成了和 x 指向的相同, 此时 y = AB(因为append方法改变的x原有的值) 而此时 b 的指向并没有发生改变。
星尘的一个朋友
2020/11/25
1.2K0
Java-值传递、地址传递
运行结果: TestParameterTransfer@15db9742 李太白 TestParameterTransfer@15db9742 李寻欢 TestParameterTransfer@15db9742 李寻欢
Fisherman渔夫
2019/07/30
1.3K0
java中值传递和引用传递
public static void main(String[] args) {int a=1;change(a);System.out.println("交换a后的值:" a);}
chenchenchen
2019/11/05
8220
Java值传递与引用传递
Java面试题: 当一个对象被当作参数传递到一个方法后,此方法可改变这个对象的属性,并可返回变化后的结果,那么这里到底是值传递还是引用传递?   答案基本上是:值传递 说明:得出这种结论的前提必须是
阳光岛主
2019/02/19
1.1K0
C中值传递与地址传递
#include <stdio.h> void print(int a) { (a)++; printf("a=%d\n",a); } int main(void) { int b = 6; print(b); printf("%d\n" ,b); return 1; } //这种情况下就属于值传递,因为作用域和内存模型的原因,生命周期消亡,数据消失,故最后数据不变。 #include <stdio.h> void print(int * a) { (*a)++; prin
用户5166556
2019/04/16
5470
java — 值传递和引用传递
  在 Java 应用程序中永远不会传递对象,而只传递对象引用。因此是按引用传递对象。Java 应用程序按引用传递对象这一事实并不意味着 Java 应用程序按引用传递参数。参数可以是对象引用,而 Java 应用程序是按值传递对象引用的。   Java 应用程序中的变量可以为以下两种类型之一:引用类型或基本类型。当作为参数传递给一个方法时,处理这两种类型的方式是相同的。两种类型都是按值传递的;没有一种按引用传递。  java实际上只有值传递,没有真正意义上的引用传递。 按值传递意味着当将一个参数传递给一
Mister24
2018/05/14
1.5K0

相似问题

在F#中传递` Pass‘参数(作为参考)

16

功能参考传递

29

参考传递UIButton

24

找不到F#库的参考组件

13

参考其他F# Jupyter笔记本

113
添加站长 进交流群

领取专属 10元无门槛券

AI混元助手 在线答疑

扫码加入开发者社群
关注 腾讯云开发者公众号

洞察 腾讯核心技术

剖析业界实践案例

扫码关注腾讯云开发者公众号
领券
问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档
查看详情【社区公告】 技术创作特训营有奖征文