#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»!