Как отправлять массивы между потоками с использованием каналов?

#c #linux #multithreading

#c #linux #многопоточность

Вопрос:

У меня есть три потока — первый читает предложения до тех пор, пока не будет задано «;», второй подсчитывает символы в этих предложениях, а третий показывает результат.

Что ж, я сделал это только для одного предложения, но отправка массивов по каналам, похоже, создает некоторые проблемы (а также чтение нескольких строк из потока).

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

Кроме того, после записи строки я получаю сообщение «write: Success».

Что здесь не так?

Это и есть код:

 #include <stdio.h>
#include <stdlib.h>
#include <sys/stat.h>
#include <unistd.h>
#include <linux/stat.h>
#include <pthread.h>
#include <string.h>

int first[2];
int second[2];

void *input(void *ptr)
{
   char str[100], ch = '0';
   int length, i = 0;

   while(1)
   {
      while(ch != ';')
      {
         printf("Enter the %d message: ", i   1);
         fflush(stdout);
         length = read(STDIN_FILENO, str, sizeof(str));

         if(write(first[1], str, sizeof(str)) != length)
         {
            perror("write");
            exit(2);
         }  

     if(length <= 0)
         {
            if(length == -1)
               perror("read");
            close(first[1]);
            exit(2);
         }

         i  ;
      }
   }
}

void *countChars(void *ptr)
{
   char str[100];
   int length, count = 0, i = 0;

   while(1)
   {
      length = read(first[0], str, sizeof(str));

      if(length <= 0)
      {
         if(length == -1)
            perror("read");
         close(first[0]);
         close(second[1]);
         exit(2);
      }
      if(write(STDOUT_FILENO, str, length) != length)
      {
         perror("write");
         exit(2);
      }

      while(str[count] != 'n') count  ;

      write(second[1], amp;count, sizeof(count));

      count = 0;
   }
}

void *output(void *ptr)
{
   int length, count = 0, i = 0;

   while(1)
   {
      length = read(second[0], amp;count, sizeof(count));
      if(length < sizeof(count))
      {
         close(second[0]);
         exit(2);
      }

      printf("Number of characters: %dn", count);
   }
}

int main()
{
   pthread_t t1, t2, t3;

   if(pipe(first) == -1)
   {
      printf("First pipe error");
      exit(1);
   }

   if(pipe(second) == -1)
   {
      printf("Second pipe error");
      exit(1);
   }

   pthread_create(amp;t1, NULL, input, NULL);
   pthread_create(amp;t2, NULL, countChars, NULL);
   pthread_create(amp;t3, NULL, output, NULL);

   pthread_join(t1, NULL);
   pthread_join(t2, NULL);
   pthread_join(t3, NULL);

   return 0;
}
 

Редактировать.

Я думаю, вопрос будет заключаться в следующем: как логически решить эту проблему? Я вижу это именно так:

 Thread1 -> (string) -> Thread2 -> (number of chars) -> Thread3 - save elements somewhere
...
Thread1 -> (ending string) -> Thread2 -> (number of chars removed later) -> Thread3 - display all elements
 

НО если это так, то как заставить потоки запускаться один за другим, как это? Как остановить приложение по конечной строке? Где сохранить эти целочисленные значения в потоке 3?

Комментарии:

1. не по теме: Почему все проголосовало против?

2. хороший вопрос, понятия не имею

3. Пожалуйста, отредактируйте свой вопрос, четко указав вашу проблему. Вы хотите исправить логику или «Запись: успех»? Потому что способ отправки данных по каналу является правильным.

4. Хорошо, я отредактировал вопрос.

Ответ №1:

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

Пример конвейера с тремя процессами. Родитель отправляет «привет, мир» дочернему элементу, который добавляет длину строки и отправляет эту новую строку внуку, который печатает ее в стандартный вывод.

 #include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <unistd.h>

void parent(int fd_write) {
    char *msg = "hello world";
    ssize_t len = strlen(msg);
    if (write(fd_write, msg, len) != len) {perror("parent write"); exit(1);}
}

void child(int fd_read, int fd_write) {
    char msg_in[100], msg_out[150];
    ssize_t len = read(fd_read, msg_in, sizeof msg_in);
    if (len == -1) {perror("child read"); exit(1);}
    msg_in[len] = '';
    len = sprintf(msg_out, "%d: %s", (int)len, msg_in);
    if (write(fd_write, msg_out, len) != len) {perror("child write"); exit(1);}
}

void grandchild(int fd_read) {
    char msg[256];
    ssize_t len = read(fd_read, msg, sizeof msg);
    if (len == -1) {perror("grandchild read"); exit(1);}
    msg[len] = '';
    printf("Msg: %sn", msg);
}

int main() {
    enum {READ, WRITE};
    pid_t pid;

    int fd[2];
    if (pipe(fd) == -1) {perror("first pipe"); exit(1);}

    pid = fork();
    if (pid == -1) {perror("first fork"); exit(1);}

    if (pid == 0) {
        int fd2[2];
        if (pipe(fd2) == -1) {perror("second pipe"); exit(1);}

        pid = fork();
        if (pid == -1) {perror("second fork"); exit(1);}

        if (pid == 0) {
            close(fd2[WRITE]);
            grandchild(fd2[READ]);
            close(fd2[READ]);
            exit(0);
        }

        close(fd[WRITE]); close(fd2[READ]);
        child(fd[READ], fd2[WRITE]);
        close(fd[READ]); close(fd2[WRITE]);
        wait(NULL);
        exit(0);
    }

    close(fd[READ]);
    parent(fd[WRITE]);
    close(fd[WRITE]);
    wait(NULL);

    return 0;
}
 

Комментарии:

1. Очень верно. Во всяком случае, вы можете передать указатель на массив через канал (который будет действителен в другом потоке того же процесса), а не на сам массив. Но даже это можно сделать более эффективно без каналов. Вам действительно нужны каналы только для процесса-процесса, а не для потока-потока.

2. Участники, проголосовавшие за понижение, не выражают причину своего несогласия.

3. IMO, вы не ответили на вопрос, вы просто изменили программу OP в соответствии со своим мнением об использовании каналов, мнение, которое я считаю неправильным для многих приложений.

4. @TranslucentPain Достаточно справедливо. Спасибо вам за комментарий. Я просто не видел смысла в трубах в случае с операцией. Я подумал, что, может быть, он имел в виду создать нормальный конвейер. Но он мог просто играть с потоками и каналами.

Ответ №2:

В потоке ввода, после вызова read length = read(STDIN_FILENO, str, sizeof(str)); , вы пишете sizeof(str) , а не о размере length .

Это должно быть

 if(write(first[1], str, length) != length)
 

Другая проблема заключается в том, что ваш код не соответствует вашей спецификации.
Вы говорите, что входной поток считывает до ‘;’, но ch никогда не изменяется в цикле. Исправьте свой код.

Комментарии:

1. О, точно, этого я не видел, спасибо. Но если это так, то цикл должен продолжаться вечно, но он останавливается после первого запуска.

2. Он останавливается после первого запуска, потому что у вас есть выход при сбое. Но из-за неправильного сопоставления длин даже успешный случай ввел путь к сбою и напечатал «write: Success»!