// threads_pairs.c // Лабораторная №7: многопоточное программирование. [file:22] // Задача: во всех парах одинаковых соседних символов второй символ заменить на пробел. [file:22] #include #include #include #include #include #include #include #include #define RBUFSZ 4096 #define WBUFSZ 4096 #define MAX_THREADS 100 typedef struct { const char *input_path; const char *output_path; long long max_repl; int thread_index; int result; } ThreadTask; static pthread_mutex_t log_mutex = PTHREAD_MUTEX_INITIALIZER; static void die_perror_thread(const char *what, const char *path, int exit_code) { int saved = errno; char msg[512]; if (path) { snprintf(msg, sizeof(msg), "%s: %s: %s\n", what, path, strerror(saved)); } else { snprintf(msg, sizeof(msg), "%s: %s\n", what, strerror(saved)); } pthread_mutex_lock(&log_mutex); write(STDERR_FILENO, msg, strlen(msg)); pthread_mutex_unlock(&log_mutex); (void) exit_code; } static long long parse_ll(const char *s) { char *end = NULL; errno = 0; long long v = strtoll(s, &end, 10); if (errno != 0 || end == s || *end != '\0' || v < 0) { errno = EINVAL; return -1; } return v; } // Обработка файла по задаче: во всех парах одинаковых символов второй заменить на пробел. [file:22] static int process_file_pairs(const char *in_path, const char *out_path, long long cap) { int in_fd = open(in_path, O_RDONLY); if (in_fd < 0) { die_perror_thread("open input failed", in_path, -1); return -1; } mode_t perms = S_IRUSR | S_IWUSR | S_IRGRP | S_IWGRP | S_IROTH | S_IWOTH; int out_fd = open(out_path, O_CREAT | O_WRONLY | O_TRUNC, perms); if (out_fd < 0) { die_perror_thread("open output failed", out_path, -1); close(in_fd); return -1; } char rbuf[RBUFSZ]; char wbuf[WBUFSZ]; size_t wlen = 0; long long total_replacements = 0; for (;;) { ssize_t n = read(in_fd, rbuf, sizeof(rbuf)); if (n > 0) { for (ssize_t i = 0; i < n; i++) { unsigned char c = (unsigned char) rbuf[i]; unsigned char outc = c; if (total_replacements < cap && i + 1 < n && (unsigned char) rbuf[i] == (unsigned char) rbuf[i + 1]) { outc = c; if (total_replacements < cap) { outc = c; // первый из пары остаётся if (wlen == sizeof(wbuf)) { ssize_t wrote = write(out_fd, wbuf, wlen); if (wrote < 0 || (size_t) wrote != wlen) { die_perror_thread("write failed", out_path, -1); close(in_fd); close(out_fd); return -1; } wlen = 0; } wbuf[wlen++] = (char) outc; i++; unsigned char c2 = (unsigned char) rbuf[i]; outc = c2; if (total_replacements < cap) { outc = ' '; total_replacements++; } } } if (wlen == sizeof(wbuf)) { ssize_t wrote = write(out_fd, wbuf, wlen); if (wrote < 0 || (size_t) wrote != wlen) { die_perror_thread("write failed", out_path, -1); close(in_fd); close(out_fd); return -1; } wlen = 0; } wbuf[wlen++] = (char) outc; } } else if (n == 0) { if (wlen > 0) { ssize_t wrote = write(out_fd, wbuf, wlen); if (wrote < 0 || (size_t) wrote != wlen) { die_perror_thread("write failed", out_path, -1); close(in_fd); close(out_fd); return -1; } wlen = 0; } break; } else { die_perror_thread("read failed", in_path, -1); close(in_fd); close(out_fd); return -1; } } if (close(in_fd) < 0) { die_perror_thread("close input failed", in_path, -1); close(out_fd); return -1; } if (close(out_fd) < 0) { die_perror_thread("close output failed", out_path, -1); return -1; } pthread_mutex_lock(&log_mutex); char res[64]; int m = snprintf(res, sizeof(res), "%lld\n", total_replacements); if (m > 0) { write(STDOUT_FILENO, res, (size_t) m); } pthread_mutex_unlock(&log_mutex); return 0; } static void *thread_func(void *arg) { ThreadTask *task = (ThreadTask *) arg; pthread_mutex_lock(&log_mutex); printf("[thread %d] start: %s -> %s, max_repl=%lld\n", task->thread_index, task->input_path, task->output_path, task->max_repl); pthread_mutex_unlock(&log_mutex); int rc = process_file_pairs(task->input_path, task->output_path, task->max_repl); task->result = rc; pthread_mutex_lock(&log_mutex); printf("[thread %d] finished with code %d\n", task->thread_index, task->result); pthread_mutex_unlock(&log_mutex); return NULL; } static void print_usage(const char *progname) { fprintf(stderr, "Usage: %s [ ...]\n", progname); fprintf(stderr, "Example: %s 100 in1.txt out1.txt in2.txt out2.txt\n", progname); } int main(int argc, char *argv[]) { if (argc < 4 || ((argc - 2) % 2) != 0) { fprintf(stderr, "ERROR: Недостаточное или неверное количество аргументов\n"); print_usage(argv[0]); return -1; } long long cap = parse_ll(argv[1]); if (cap < 0) { die_perror_thread("invalid max_replacements", argv[1], -1); return -1; } int num_files = (argc - 2) / 2; if (num_files > MAX_THREADS) { fprintf(stderr, "ERROR: Слишком много файлов (максимум %d пар)\n", MAX_THREADS); return -1; } printf("=== Многопоточная обработка: пары одинаковых символов ===\n"); printf("Главный поток TID: %lu\n", (unsigned long) pthread_self()); printf("Максимум замен на файл: %lld\n", cap); printf("Количество файловых пар: %d\n\n", num_files); pthread_t threads[MAX_THREADS]; ThreadTask tasks[MAX_THREADS]; for (int i = 0; i < num_files; i++) { const char *input_path = argv[2 + i * 2]; const char *output_path = argv[3 + i * 2]; tasks[i].input_path = input_path; tasks[i].output_path = output_path; tasks[i].max_repl = cap; tasks[i].thread_index = i + 1; tasks[i].result = -1; int rc = pthread_create(&threads[i], NULL, thread_func, &tasks[i]); if (rc != 0) { errno = rc; die_perror_thread("pthread_create failed", NULL, -1); tasks[i].result = -1; } } int success_count = 0; int error_count = 0; for (int i = 0; i < num_files; i++) { if (!threads[i]) { error_count++; continue; } int rc = pthread_join(threads[i], NULL); if (rc != 0) { errno = rc; die_perror_thread("pthread_join failed", NULL, -1); error_count++; continue; } if (tasks[i].result == 0) { success_count++; } else { error_count++; } } printf("\n=== Итоговая статистика ===\n"); printf("Всего потоков: %d\n", num_files); printf("Успешно завершено: %d\n", success_count); printf("С ошибкой: %d\n", error_count); if (error_count > 0) { printf("\nОБЩИЙ СТАТУС: Завершено с ошибками\n"); return -1; } else { printf("\nОБЩИЙ СТАТУС: Все потоки завершены успешно\n"); return 0; } }