Задание 9 параллельная обработка файлов

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

Рекомендуется для каждого файла создать структуру со следующими полями: имя файла, символ, флаг о наличии ошибок и количество символов. Рекомендуется написать функцию, которая получает указать на эту структуру в качестве параметра и не должна использовать глобальные данные. При выполнении функция должна заполнить поля — флаг о наличии/отсутствии ошибки и количество символов. Не рекомендуется, чтобы функция выводила какие-либо сообщения на экран (по крайней мере, в окончательном варианте).

Программа должна иметь следующую структуру:

  1. Создание массива структур для каждого файла.
  2. Исполнение функции с параметром-структурой в цикле.
  3. Вывод данных, накопленных в структурах.

Сначала необходимо выполнить программу без использования потоков и убедиться в её работоспособности.

Затем её нужно модифицировать так, чтобы функции выполнялись в отдельных потоках. Структура программы будет следующая:

  1. Создание массива структур для каждого файла.
  2. Создание потока с функции и параметром-структурой в цикле.
  3. Ожидание завершения потоков.
  4. Вывод данных, накопленных в структурах.

Файлы нужно предварительно создать, они должны быть достаточно большими (> 10M). Следует сравнить быстродействие обоих вариантов программы.

Полезные ссылки

Полезные заголовки

#include <pthread.h>

Полезные функции

  • Создание нового потока:

    int pthread_create(
      pthread_t *restrict thread,
      const pthread_attr_t *restrict attr,
      void *(*start_routine)(void *),
      void *restrict arg);
    
  • Прекращение выполнения потока:

    void pthread_exit(void *value_ptr);
    
  • Получение идентификатора текущего потока:

    pthread_t pthread_self(void);
    
  • Посылка сигнала потоку:

    int pthread_kill(pthread_t thread, int sig);
    
  • Прекращение потока:

    int pthread_cancel(pthread_t thread);
    
  • Приостановка текущего потока до завершения потока с идентификатором thread:

    int pthread_join(pthread_t thread, void **value_ptr);
    
  • Проверка того, что t1 и t2 идентифицируют один и тот же поток:

    int pthread_equal(pthread_t t1, pthread_t t2);
    
  • Создание объекта для установки атрибутов потока:

    int pthread_attr_init(pthread_attr_t *attr);            
    
  • Удаление объекта для установки атрибутов потока:

    int pthread_attr_destroy(pthread_attr_t *attr);
    
  • Установка размеров стека для потока:

    int pthread_attr_getstacksize(
      const pthread_attr_t *restrict attr,
      size_t *restrict stacksize);
    
    int pthread_attr_setstacksize(
      pthread_attr_t *attr,
      size_t stacksize);
    

    (и другие pthread_attr_*-функции.)

  • Выполнение инициализации мьютекса:

    int pthread_mutex_init(
      pthread_mutex_t *restrict mutex,
      const pthread_mutexattr_t *restrict attr);
    
  • Удаление мьютекса:

    int pthread_mutex_destroy(pthread_mutex_t *mutex);
    
  • Наложение мюьтекса:

    int pthread_mutex_lock(pthread_mutex_t *mutex);
    
  • Освобождение мьютекса:

    int pthread_mutex_unlock(pthread_mutex_t *mutex);
    
  • Попытка наложения мьютекса без блокировки текущего потока:

    int pthread_mutex_trylock(pthread_mutex_t *mutex);