это безопасно для записи в файл в другом потоке? - PullRequest
1 голос
/ 12 января 2020

Я не знаю, если это нормально, но он компилируется:

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 в parent, но я не знаю, должен ли я также pthread_join объединять потоки или это неявно wait(). Однако безопасно ли записывать один и тот же файл в два потока? Я запускаю несколько раз, а иногда получаю 1) parentchild, но иногда только 2) parent, других случаев нет - я не знаю, почему child не писал так же, когда родительский интерфейс wait () для этого. Может кто-нибудь объяснить, почему эти выводы?

Ответы [ 2 ]

3 голосов
/ 12 января 2020

Вам необходимо вызвать pthread_join () в дочернем процессе, чтобы избежать возможных состояний гонки во время последовательности выхода дочернего процесса (например, дочерний процесс может завершиться, прежде чем его поток получит возможность записи в файл). Вызов pthread_join () в родительском процессе не поможет,

Что касается файла, запись в него обоих процессов безопасна в том смысле, что он не вызовет cra sh, но порядок в котором данные записываются в файл, будут неопределенными, так как два процесса выполняются одновременно.

1 голос
/ 12 января 2020

Не знаю, нормально ли это, но он компилируется:

Без каких-либо предупреждений? В самом деле? Я предполагаю, что код, который вы компилируете, должен включать все необходимые заголовки (иначе у вас должно быть загружает предупреждений), но если ваш компилятор не может быть убежден, чтобы определить

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

тогда это не стоит его соли. Действительно, переменная arg является используемой неинициализированной, и поэтому ваша программа демонстрирует неопределенное поведение.

Но даже если вы исправите это, после чего программа может быть скомпилирована без предупреждений, она все еще не в порядке.

Я - wait () int в parent, но я не знаю, должен ли я также pthread_join объединять потоки, или это неявно wait().

Родительский процесс вызывает wait(). Это ожидает завершения дочернего процесса, если таковые имеются. Период. Это не имеет значения для поведения дочернего элемента до его завершения.

Кроме того, в программе pthreads основной поток является специальным: , когда он завершается, вся программа завершается, включая все другие потоки. . Таким образом, ваш дочерний процесс страдает от состояния гонки: основной поток завершает работу сразу после создания второго потока, не гарантируя, что другой поток завершает работу первым, поэтому не определено, что, если таковые имеются, поведение второго потока фактически выполняется. Чтобы избежать этой проблемы, да, в дочернем процессе основной поток должен присоединиться к другому, прежде чем завершится.

Однако безопасно ли даже запись в один и тот же файл в двух потоках?

Это зависит - как от обстоятельств, так и от того, что вы подразумеваете под «безопасным». POSIX требует, чтобы функция write() была поточно-ориентированной, но это не означает, что несколько потоков или процессов, записывающих в один и тот же файл, не могут по-прежнему мешать друг другу, перезаписывая выходные данные друг друга.

Ваш отчасти тем не менее, в особом случае родитель и потомок записывают через одно и то же описание открытого файла в ядре, а потомок унаследовал с ним связь от своего родителя. Согласно POSIX, вы должны увидеть выходные данные обоих процессов (если они есть; см. Выше) в файле. Однако в POSIX нет способа предсказать порядок появления этих выходных данных.

Я запускаю несколько раз, и иногда вывод равен 1) parentchild, а иногда только 2) parent, никаких других случаи - я не знаю, почему child не написал так же, как родители ждут этого. Может кто-нибудь объяснить, почему эти выходные данные?

Дочерний процесс может завершиться до того, как его второй поток выполнит свою запись. В этом случае вы увидите только выходные данные родителя, а не ребенка.

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...