如何使用多线程在 C 中读取包含类似 json 对象的文件

问题描述 投票:0回答:1

我正在尝试使用 C 语言的多线程读取文件来读取文件,但是当我根据文件大小划分块时,有些块可能会在行的中间开始/结束。我试图调整块大小以防发生这种情况。这些线的大小不一样。我正在尝试读取大约 100 MB 的非常大的文件。

首先,这是一个好方法还是应该将多线程仅用于其他任务,例如处理线路?

我的方法是移动块的开头和结尾,直到找到一行的结尾。这样,如果一个块恰好位于一行内(当线程数 > 行数时),开始和结束将匹配,我将不会启动该线程。大多数时候它读取得很好,但有时,一行没有被读取,或者一行被同一个线程读取两次,或者一行的一小部分被另一个线程读取。

不同线程同时读取同一个文件有什么问题吗?

long chunk_size = file_size / num_threads;

pthread_t threads[num_threads];
ThreadData thread_data[num_threads];

long last_end = 0;

for (uint32_t i = 0; i < num_threads; ++i)
{
    thread_data[i].stats = stats;
    thread_data[i].thread_tweets = NULL;
    thread_data[i].failed = 0;

    thread_data[i].file = file;
    thread_data[i].start = i * chunk_size;
    thread_data[i].end = (i == num_threads - 1) ? file_size : (i + 1) * chunk_size;
    
    if (i > 0)
    {
        if (thread_data[i].end < thread_data[i - 1].start)
        {
            thread_data[i].failed = 1;
            continue;
        }
    }
    int ch;
    // Adjust start position to the beginning of the next line
    if (!is_start_at_line_boundary(file, thread_data[i].start))
    {
        fseek(file, thread_data[i].start, SEEK_SET);
        while ((ch = fgetc(file)) != '\n' && ch != EOF);
        thread_data[i].start = ftell(file);
    }

    // Adjust end position to the end of the line
    fseek(file, thread_data[i].end, SEEK_SET);
    while ((ch = fgetc(file)) != '\n' && ch != EOF);
    thread_data[i].end = ftell(file);
    if (ch != '\n' && ch != EOF)
    {
        thread_data[i].end++;
    }
    // If they coincide, the chunk was inside a line and the thread shoudnt run
    if (thread_data[i].end == thread_data[i].start)
    {
        thread_data[i].failed = 1;
        continue;
    }
    if (i > 0)
    { 
        thread_data[i].start = last_end;
    }
    if (pthread_create(&threads[i], NULL, read_file_chunk, &thread_data[i]))
    {
        fprintf(stderr, "Error creating thread\n");
        exit(EXIT_FAILURE);
    }
    last_end = thread_data[i].end;
}
int is_start_at_line_boundary(FILE *file, long start)
{
    if (start == 0)
    {
        return 1; // Start of the file
    }
    fseek(file, start - 1, SEEK_SET);
    if (fgetc(file) == '\n')
    {
        return 1; // Start is at the beginning of a line
    }
    return 0;
}

函数 read_file_chunk 将使用

fseek()
转到块的开头,并使用
fgets()
读取整个块,其中它为每一行调用解析函数,因为每行包含一个单独的 json 格式,而不是
 ,
;
,例如:

{"created_at": "2020-01-14 12:00:00"; "hashtags": ["A", "B"]; "id": 546542; "uid": 1500}

我应该使用 json 库而不是假设每一行都是一个 json 对象吗?这样会更高效、更明智吗?

c multithreading chunks file-read
1个回答
0
投票

我正在尝试读取大约 100 MB 的非常大的文件。首先,这是一个好方法还是应该将多线程仅用于其他任务,例如处理线路?

非常粗略地说,典型的硬盘读取速度为 150 MB/s。对于 SSD,可能为 400 MB/s。 许多因素会影响实际吞吐量,但我认为可以安全地假设 100 MB 文件的单线程顺序读取时间不应超过 1 秒。 如果文件是“热”文件(由文件系统或操作系统缓存),速度可能会快得多。

HDD 和 SSD 都是顺序设备,因此即使有多个线程尝试同时读取文件的不同块,每个线程也可能必须等待其他线程的读取。

在我的实验中,如果您需要将整个文件读入内存,最简单也是最快的方法:对整个文件发出一次读取。

(如果您希望可能不需要将整个文件放入内存和/或需要乱序读取文件,则将文件映射到内存可能是一个好主意。这两种情况似乎都不适合您的情况。)

谨防过早优化。 从一个有效的单线程实现开始。

如果速度不够快,那么你必须进行测量以确定瓶颈在哪里。 在您的情况下,这可能是将文件读入内存所需的时间,也可能是 JSON 解析的时间。

如果读取文件是瓶颈,我会寻找多线程以外的解决方案。

如果解析 JSON 是瓶颈,那么多线程可能是值得探索的一种可能的解决方案。 在这种情况下,我最初会保留单线程 I/O 不变,并将多线程应用于数据解析。

(我并不否认在某些情况下你可能想将阅读内容分成几个部分,但我不会从那里开始。)

© www.soinside.com 2019 - 2024. All rights reserved.