【问题标题】:is it safe to write to a file in another thread?在另一个线程中写入文件是否安全?
【发布时间】:2020-04-29 10:00:56
【问题描述】:

我不知道,如果这样可以,但它编译:

typedef struct
{
   int fd;
   char *str;
   int c;
} ARG;

void *ww(void *arg){
   ARG *a = (ARG *)arg;
   write(a->fd,a->str,a->c);

   return NULL;
}


int main (void) {

   int fd = open("./smf", O_CREAT|O_WRONLY|O_TRUNC, S_IRWXU);
   int ch = fork();

   if (ch==0){
      ARG *arg; pthread_t p1;
      arg->fd = fd;
      arg->str = malloc(6);
      strcpy(arg->str, "child");
      arg->c = 6;

      pthread_create( &p1, NULL, ww, arg);
   } else {
      write(fd, "parent\0", 7);
      wait(NULL);
   }

   return 0;
}

我是父级的wait()int,但我不知道我是否也应该pthread_join 来合并线程,或者它是由wait() 隐含的。但是,在两个线程中写入同一个文件是否安全?我跑了几次,有时输出是 1)parentchild,但有时只有 2)parent,没有其他情况 - 我不知道为什么孩子在父母等待()时写得不好。有人可以解释为什么这些输出吗?

【问题讨论】:

  • 贴出的代码无法处理调用fork()失败的情况
  • 这一行; pthread_create( &p1, NULL, ww, arg); 后面应该跟 pthread_join( p1 );exit( EXIT_SUCCESS );
  • 关于这个参数:S_IRWXU) X 使输出文件可执行,这似乎有点过头了。
  • 关于:"parent\0" \0 是一个 NUL 字节,编译器也会附加一个 NUL 字节。最后不需要一对 NUL 字节。
  • I/O 流stdout 被缓冲。数据仅在特定条件下传递到终端。强烈建议在每次调用 write() 后立即加上:fflush() 声明。

标签: c multithreading io


【解决方案1】:

您需要在子进程中调用 pthread_join() 以避免子进程退出序列期间的潜在竞争条件(例如,子进程可以在其线程有机会写入文件之前退出)。在父进程中调用 pthread_join() 也无济于事,

对于文件,让两个进程都写入它是安全的,因为它不会导致崩溃,但是数据写入文件的顺序将是不确定的,因为这两个进程是同时执行的.

【讨论】:

  • 但它有什么不同,当我为新进程调用 fork() 时,它也是新线程吗?它应该与父级具有相同的内存空间,或者?所以当我打电话给pthread_create 时,它是来自已经 fork()ed 的另一个线程吗?因为我不明白为什么我不能在父母中phtread_join,但在创建后必须在孩子中
  • fork() 创建一个子进程,它是调用实例 fork() 时父进程的副本。因此,与父进程一样,您的子进程将只有在 main() 开始执行的“默认线程”。然后您的子进程继续调用 pthread_create(),它在子进程中(但不在父进程中)产生一个线程,因此需要在子进程中调用 pthread_join()(但不在父进程中)跨度>
【解决方案2】:

我不知道,如果这样可以,但它编译:

甚至没有任何警告?真的吗?我想你正在编译的代码必须包含所有需要的头文件(否则你应该有 loads 的警告),但是如果你的编译器不能被说服发现

buggy.c:30:15: warning: ‘arg’ may be used uninitialized in this
function [-Wmaybe-uninitialized]
       arg->fd = fd;
             ^

那么它不值得盐。事实上,变量arg 未初始化使用,因此您的程序表现出未定义的行为。

但即使你解决了这个问题,之后程序可以编译而没有警告,它仍然不行。

我在父母中是 wait()int,但我不知道我是否也应该 pthread_join 用于合并线程或由wait() 隐含。

父进程正在调用wait()。这等待子进程终止,如果有的话。时期。它对终止前孩子的行为没有影响。

此外,在 pthreads 程序中,主线程是特殊的:when it terminates, the whole program terminates, including all other threads。因此,您的子进程会遇到竞争条件:主线程在创建第二个线程后立即终止,而不能确保另一个线程首先终止,因此未定义第二个线程的实际行为(如果有的话)。为了避免这个问题,是的,在子进程中,主线程应该在自己终止之前加入另一个。

然而 在两个线程中写入同一个文件是否安全?

这取决于环境和您所说的“安全”。 POSIX 要求 write() 函数是线程安全的,但这并不意味着写入同一文件的多个线程或进程仍然不能通过覆盖彼此的输出来相互干扰。

你的情况有点特殊,然而,父母和孩子通过内核中相同的打开文件描述进行写入,孩子继承了与其父母的关联。根据 POSIX,您应该在文件中看到两个进程的输出(如果有的话;见上文)。但是,POSIX 无法预测这些输出的出现顺序。

我很少跑 次,有时输出是 1) parentchild 但有时只有 2) parent,没有其他情况-我不知道为什么孩子也不写 当父母等待()时。有人可以解释为什么这些 输出?

子进程可以在其第二个线程执行写入之前终止。在这种情况下,您只会看到父级的输出,而不是子级的输出。

【讨论】:

  • 我知道arg->fd 没有初始化,但不知道为什么。 arg->fd = fd.
  • 竞态条件是如何工作的?如果我不加入孩子,但父母仍然为我等待(),那么为什么第二个线程不会终止?
  • 和最后一个:yours is special - the same descriptor,所以您是说,无论有多少线程写入特定文件描述符,它们都不会相互覆盖? (这就是我理解你声称的方式),但是对于其他构造,例如 FILE *(例如在 fopen 中),有可能覆盖输出?
  • @Herdsman,关于arg,你没有在声明中提供初始化器,所以它没有被初始化。在尝试取消引用其(指针)值以写入它假设指向的对象之前,您也不会随后为其分配值。可能您想将arg 声明为ARG,而不是指向它的指针,并相应地调整您对它的使用。
  • @Herdsman,我说的是同一个打开文件描述,不是同一个文件描述符。这些是不同的东西,尽管是相关的。前者是内核数据结构,后者是一个数字,不一定唯一,指的是某个特定进程范围内的打开文件描述。 FILE 对象或指向一个对象的指针又是不同的东西。我的 cmets 专门指使用 write() 函数,仅此而已。
猜你喜欢
  • 1970-01-01
  • 2020-08-03
  • 2011-07-14
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2019-02-12
  • 1970-01-01
  • 1970-01-01
相关资源
最近更新 更多