在 fopen a+ 模式下从多个线程/进程写入锁定文件之前是否需要同步/刷新?

2023-11-30

我正在从多个线程对单个文件执行 I/O。访问此共享文件foo通过咨询文件锁进行控制(flock(2) with LOCK_EX). foo被打开fopen(3) mode a+. a+被选中是因为文档指出:

对文件的后续写入将始终以当时的当前位置结束 文件结尾,无论有任何干扰fseek(3)或类似的。

简化后,操作将开始:

FILE *fp = fopen("foo", "a+");
...spawn threads...

写作将继续:

flock(fileno(fp), LOCK_EX);
fwrite(buffer, buffer_size, 1, fp);
flock(fileno(fp), LOCK_UN);

我目前没有任何fflush(3) or fsync(2)之前调用fwrite(3)我想知道我是否应该这样做。是否fopen(3) a+模式在计算“当前 EOF”时是否考虑多个线程命中文件?我知道flock(2)当有未完成的 I/O 时,授予我锁定可能没有问题。

在我有限的测试中(在多个线程中写入很长的 ASCII 文本行,然后在多线程中写入换行符,然后确保结果文件中每行的字符数相等),我没有看到任何“损坏”使用fflush(3) or fsync(2)。它们的存在大大降低了 I/O 性能。

长话短说: 使用文件锁时,在写入打开于的多个线程之间的共享文件之前是否需要刷新流a+模式?多个分叉/不同的机器将文件写入并行文件系统?

可能相关:为什么在读/写“+”模式下读写之间总是需要fseek或fflush


这是错误的锁类型。flock is only用于进程之间的锁定,而不是同一进程中的线程之间的锁定。从man 2 flock:



A call to flock() may block if an incompatible lock is held by  another
process.   To  make  a  nonblocking request, include LOCK_NB (by ORing)
with any of the above operations.  

添加了强调。和...



A process may only hold one type of lock (shared  or  exclusive)  on  a
file.   Subsequent flock() calls on an already locked file will convert
an existing lock to the new lock mode.
  

你想使用flockfile相反(或者另外,如果也使用多个进程)。这flockfile函数用于控制对FILE *来自多个线程。从手册页:



The stdio functions are thread-safe.  This is achieved by assigning  to
each  FILE object a lockcount and (if the lockcount is nonzero) an own‐
ing thread.  For each library call, these functions wait until the FILE
object  is no longer locked by a different thread, then lock it, do the
requested I/O, and unlock the object again.

(Note: this locking has nothing to do with the  file  locking  done  by
functions like flock(2) and lockf(3).)
  

像这样:

// in one of the threads...
flockfile(fp);
fwrite(..., fp);
funlockfile(fp);

好消息是glibc,如果每个关键部分中只有一个来自 stdio.h 的函数调用,则不需要锁定文件,因为glibc has a fwrite那个锁。但在其他平台上情况并非如此,锁定文件当然不会有什么坏处。所以如果你在 Linux 上运行,你永远不会注意到flock没有做你想做的事,因为fwrite自动执行。

关于追加模式:使用附加模式写入时不需要额外的刷新,除非您想确保打开相同文件的不同进程(或一个进程对同一文件有多个句柄)之间的顺序。除非您正在读取文件,否则不需要“a+”模式。

示范flock

如果你不相信我flock不提供使用相同文件描述符的线程之间的线程安全,这里是一个演示程序。

#include <stdio.h>
#include <errno.h>
#include <pthread.h>
#include <string.h>
#include <stdlib.h>
#include <sys/file.h>

static FILE *fp;
static pthread_mutex_t mutex;
static pthread_cond_t cond;
int state;

static void fail_func(int code, const char *func, int line)
{
    fprintf(stderr, "%s:%d: error: %s\n", func, line, strerror(code));
    exit(1);
}

#define fail(code) fail_func(code, __FUNCTION__, __LINE__)

void *thread1(void *p)
{
    int r;

    // Lock file (thread 2 does not have lock yet)
    r = pthread_mutex_lock(&mutex);
    if (r) fail(r);
    r = flock(fileno(fp), LOCK_EX);
    if (r) fail(errno);
    puts("thread1: flock successful");
    state = 1;
    r = pthread_mutex_unlock(&mutex);
    if (r) fail(r);

    // Wake thread 2
    r = pthread_cond_signal(&cond);
    if (r) fail(r);

    // Wait for thread 2
    r = pthread_mutex_lock(&mutex);
    if (r) fail(r);
    while (state != 2) {
        r = pthread_cond_wait(&cond, &mutex);
        if (r) fail(r);
    }
    puts("thread1: exiting");
    r = pthread_mutex_unlock(&mutex);
    if (r) fail(r);

    return NULL;
}

void *thread2(void *p)
{
    int r;

    // Wait for thread 1
    r = pthread_mutex_lock(&mutex);
    if (r) fail(r);
    while (state != 1) {
        r = pthread_cond_wait(&cond, &mutex);
        if (r) fail(r);
    }

    // Also lock file (thread 1 already has lock)
    r = flock(fileno(fp), LOCK_EX);
    if (r) fail(r);
    puts("thread2: flock successful");

    // Wake thread 1
    state = 2;
    puts("thread2: exiting");
    r = pthread_mutex_unlock(&mutex);
    if (r) fail(r);
    r = pthread_cond_signal(&cond);
    if (r) fail(r);

    return NULL;
}

int main(int argc, char *argv[])
{
    pthread_t t1, t2;
    void *ret;
    int r;

    r = pthread_mutex_init(&mutex, NULL);
    if (r) fail(r);
    r = pthread_cond_init(&cond, NULL);
    if (r) fail(r);
    fp = fopen("flockfile.txt", "a");
    if (!fp) fail(errno);
    r = pthread_create(&t1, NULL, thread1, NULL);
    if (r) fail(r);
    r = pthread_create(&t2, NULL, thread2, NULL);
    if (r) fail(r);
    r = pthread_join(t1, &ret);
    if (r) fail(r);
    r = pthread_join(t2, &ret);
    if (r) fail(r);
    puts("done");
    return 0;
}

在我的系统上,它产生以下输出:



thread1: flock successful
thread2: flock successful
thread2: exiting
thread1: exiting
done
  

注意线程1并没有释放flock,并且线程 2 无论如何都能够获取它。使用条件变量可确保线程 1 在线程 2 获取锁之前不会退出。这正是flock手册页说,因为flock表示锁是每个文件和每个进程的,但不是每个线程的。

以原子方式追加到文件的摘要

为了在进程和线程之间进行原子写入,您可以执行以下两项简单操作之一:

  • Use write并且写不超过PIPE_BUF字节。PIPE_BUF定义于<limits.h>,在我的系统上是 4096。如果文件描述符在以下位置打开O_APPEND模式,那么写入将自动到达文件末尾,无论其他人正在写入文件(线程和/或进程)。

  • Use write and flock。如果你ever写多于PIPE_BUF一次字节,这是所有写入的唯一选择。同样,如果文件打开于O_APPEND模式,那么字节将转到文件末尾。这将以原子方式发生,但仅从每个有能力的人的角度来看flock.

此外,

  • 如果你使用<stdio.h>并分享一个FILE *在线程之间,您还需要调用flockfile来自每个线程。如果您使用较低级别的 POSIX API (open/write/ETC)。如果您使用,这也不需要glibc每次写入都是一个函数调用(例如,您想要原子地fputs).

  • 如果只使用一个进程,flock不需要。

本文内容由网友自发贡献,版权归原作者所有,本站不承担相应法律责任。如您发现有涉嫌抄袭侵权的内容,请联系:hwhale#tublm.com(使用前将#替换为@)

在 fopen a+ 模式下从多个线程/进程写入锁定文件之前是否需要同步/刷新? 的相关文章

随机推荐

  • C# 中的全局 Keyhook

    我想制作一个新的应用程序 我可以通过外部 GUI 的按键进行控制 如果按下该键 首先应该有一个文本字段发生变化 最后我想让计时器在按键时启动 我用谷歌搜索并访问了 Stack Overflow 但没有找到任何可以真正帮助我解决问题的方法 我
  • d3 径向条形图中的标签放置

    在我的 d3 径向图表中 我试图将标签文本置于段弧上方 而不是保留在外圆之外 Fiddle var width 360 height 300 barHeight height 2 40 var formatNumber d3 format
  • 带样条+日期时间对象的平滑线不起作用

    我一直在努力让情节更加流畅 就像完成的一样here 但我的 X 是与 linspace 不兼容的日期时间对象 我将 X 转换为 matplotlib 日期 Xnew matplotlib dates date2num X X smooth
  • Android 上的 Google 收藏集

    有人在 Android 上使用过 Multimaps 吗 Guava 在 Android 上按原样运行 您遇到了什么问题 使用已发布的 JAR 而不是 Guava 源 与往常一样 您应该在构建过程中使用 ProGuard 来缩小最终二进制文
  • 我们可以将 SQL 从 S3 存储桶导入到 AWS ec2(实例)吗?

    我正在尝试将 SQL 文件从 S3 存储桶导入到 EC2 实例 SQL 文件是可公开访问的 并且在实例中安装的 mysql 客户端的帮助下 我正在执行以下命令 mysql gt source https s3 ap southeast 1
  • 有没有办法给一个节点添加多个标签?

    假设我正在创建一个关于食物的数据库 在其中我想添加木豆 它既是咖喱又是扁豆 SELECT FROM cypher menu CREATE LENTIL CURRY name Dal AS dal agtype 2023 02 20 06 4
  • TestNG错误-java.lang.NoSuchMethodError: org.testng.TestRunner.addListener(Ljava/lang/Object;)V

    我安装的testNG版本是 6 9 11 我的pom xml有 6 14 3 Eclipse版本neon 4 6 3 当我通过 TestNG 运行课程时 出现以下错误 有人可以帮我解决这个问题吗 java lang NoSuchMethod
  • 如果任何函数并行失败,则停止 bash

    我有一个 BASH 可以在我的 BASH 中并行运行 3 个函数 functionA my command echo ERROR gt gt LOG FILE exit 1 functionB my command echo ERROR g
  • 如何自定义@FeignClient Expander来转换参数?

    Feign默认扩展器转换参数 final class ToStringExpander implements Expander Override public String expand Object value return value
  • 如何在 Picturebox C# 中添加标签透明度?

    我在其中创建一个程序可以添加标签和图片框 所有控件都必须是面板的子控件 我使用这样的代码 panel2 Controls Add picturebox1 panel2 Controls Add label1 是的 问题是我想要在图片框上贴上
  • java控制台输入

    通过控制台输入的任何数据类型 就像我使用 BufferedReader 类所做的那样 是 String 之后我们将其转换为所需的数据类型 如 Inter parseInt 表示整数 但是在 C 中 我们可以接受任何输入原始数据类型 而在ja
  • std::auto_ptr 到 std::unique_ptr

    随着新标准的到来 以及某些编译器中已经可用的部分 新类型std unique ptr应该是替代品std auto ptr 它们的用法是否完全重叠 这样我可以在我的代码上进行全局查找 替换 不是我会这样做 但如果我这样做 或者我应该意识到一些
  • 搜索短信收件箱

    如何搜索短信收件箱并显示来自特殊号码的最新消息 例如 搜索 999999999 并显示从此号码收到的最后一条消息 有办法做到这一点吗 我已使用此代码返回我的收件箱中的消息数 TextView view Override public voi
  • System.Object 类和结构之间的关系

    我知道我的问题看起来很愚蠢 但我很困惑 如果有人为我澄清这一点 我将不胜感激 我知道结构 例如Int32 是值类型 在堆栈上实例化 而类是引用类型 在堆上实例化 我还知道所有结构都派生自 System Object 类型 它是一个类 我想知
  • 尽管已颁发有效令牌,但仍从 Dynamics 365 返回 401

    我正在尝试重写当前使用 JavaScript 连接到 Dynamics 365 的客户端应用程序 let URL https
  • Ember 2.0 中的 makeBoundHelper 替代方案

    到目前为止 我一直在使用绑定助手在我的博客文章中注入 Google DFP 广告 由于所有 Handlebars API 已在 Ember 2 0 中删除从 Ember 2 0 开始我可以使用什么 import Ember from emb
  • Android 加速度计全系列

    我目前正在处理 Android 传感器 API 和加速计数据 虽然内部组件应该支持高加速 甚至 16g 但我尝试了一些最近的智能手机 发现暴露数据的最大范围通常是 2g 例如Nexus 5 有时是 4g 例如Nexus 4 有没有办法设置传
  • add_library 无法在 CMake 中添加 CSharp 源文件?

    我正在尝试从一组构建 CSharp DLL cs文件 我用的是add library 函数添加源文件 cs文件 但它给出了诸如 add library for library libname without any source files
  • phonegap :: navigator.notification.activityStart()和loadingStart()不起作用

    我尝试在phonegap1 0 在onDeviceReady内部 上调用loadingStart 和activityStart 但它不起作用 有已知的原因吗 它应该运作良好吗 thnx 正如 mmigdol 所说 这些已在 1 0 0 中被
  • 在 fopen a+ 模式下从多个线程/进程写入锁定文件之前是否需要同步/刷新?

    我正在从多个线程对单个文件执行 I O 访问此共享文件foo通过咨询文件锁进行控制 flock 2 with LOCK EX foo被打开fopen 3 mode a a 被选中是因为文档指出 对文件的后续写入将始终以当时的当前位置结束 文