litbaza книги онлайнРазная литератураCrystal Programming. Введение на основе проекта в создание эффективных, безопасных и читаемых веб-приложений и приложений CLI - Джордж Дитрих

Шрифт:

-
+

Интервал:

-
+

Закладка:

Сделать
1 ... 29 30 31 32 33 34 35 36 37 ... 75
Перейти на страницу:
поскольку мы можем добавить еще один метод к типу Processor, чтобы также предоставить его для использования библиотекой. Во-первых, давайте начнем с метода Processor. Откройте src/processor.cr и добавьте в него следующий метод:

def process_multiple(filter : String, input_files :

  Array(String), error : IO) : Nil

    input_files.each do |file|

      File.open(file, "r") do |input_file|

        File.open("#{input_file.path}.transformed", "w") do

          |output_file|

          self.process [filter], input_file, output_file, error

        end

      end

    end

  end

Этот метод сводится к следующим шагам:

1. Определите новый метод, предназначенный для обработки нескольких входных файлов, который принимает фильтр и массив файлов для обработки.

2. Переберите каждый входной файл, используя метод File.open, чтобы открыть файл для чтения.

3. Снова используйте File.open, чтобы открыть выходной файл для записи, используя путь к входному файлу с добавлением .transformed в качестве имени выходного файла,

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

Прежде чем мы сможем это протестировать, нам нужно сделать так, чтобы передача опции --multi заставляла CLI вызывать этот метод. Давайте сделаем это сейчас. Откройте src/transform_cli.cr и обновите его, чтобы он выглядел следующим образом:

require "./transform"

require "option_parser"

processor = Transform::Processor.new

multi_file_mode = false

OptionParser.parse do |parser|

  parser.banner = "Usage: transform <filter> [options]

    [arguments] [filename …]"

  parser.on("-m", "--multi", "Enables multiple file input mode") { multi_file_mode = true }

  parser.on("-h", "--help", "Show this help") do

    puts parser

    exit

  end

end

begin

  if multi_file_mode

    processor.process_multiple ARGV.shift, ARGV, STDERR

  else

    processor.process ARGV, STDIN, STDOUT, STDERR

  end

rescue ex : RuntimeError

  exit 1

end

И снова на помощь приходит стандартная библиотека Crystal в виде типа OptionParser. Этот тип позволяет вам настроить логику, которая должна выполняться, когда эти параметры передаются через ARGV. В нашем случае мы можем использовать это для определения более удобного интерфейса, который также будет поддерживать параметры -h или --help. Кроме того, он позволяет вам реагировать на флаг --multi без необходимости вручную анализировать ARGV. Код довольно прост. Если флаг передан, мы устанавливаем для переменной multi_file_mode значение true, которое используется для определения того, какой метод процессора вызывать.

Чтобы проверить это, я создал несколько простых файлов YAML в корневом каталоге проекта. Не имеет большого значения, что они собой представляют, важно лишь то, что они действительны в формате YAML. Затем я собрал наш двоичный файл и запустил его с помощью ./bin/transform --multi. file1.yml file2.yml file3.yml, утверждая, что три выходных файла были созданы должным образом. У меня это заняло ~0,1 секунды. Давайте посмотрим, сможем ли мы улучшить это, реализовав параллельную версию метода process_multiple.

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

def process_multiple(filter : String, input_files :

  Array(String), error : IO) : Nil

  channel = Channel(Bool).new

  input_files.each do |file|

    spawn do

      File.open(file, "r") do |input_file|

        File.open("#{input_file.path}.transformed", "w")

          do |output_file|

          self.process [filter], input_file, output_file, error

        end

      end

    ensure

      channel.send true

    end

  end

  input_files.size.times do

    channel.receive

  end

end

По сути, это то же самое, только с введением волокон для параллельности. Назначение канала — гарантировать, что основное волокно не выйдет из строя до завершения обработки всех файлов. Это достигается путем отправки значения true в канал после обработки файла и получения этого значения ожидаемое количество раз. Команда send находится внутри блока ensure для обработки сценария в случае сбоя процесса. Эта реализация требует немного большей доработки и будет рассмотрена в следующей главе. Я провел тот же тест, что и раньше, с параллельным кодом и получил значение от 0,03 до 0,06 секунды.

Я бы в любой день взял прирост производительности в 2-3 раза.

Резюме

И вот оно: одновременная обработка нескольких входных файлов! Параллельное программирование может быть ценным инструментом для создания высокопроизводительных приложений, позволяя разбивать рабочие нагрузки, связанные с IO, так, чтобы некоторая часть работы выполнялась постоянно. Кроме того, его можно использовать для уменьшения объема памяти приложения за счет одновременной обработки входных данных по мере их поступления, без необходимости ждать и загружать все данные в память.

На данный момент наш CLI почти готов! Теперь он может эффективно обрабатывать как одиночные, так и множественные входные файлы. Он может передавать данные в потоковом режиме, чтобы уменьшить использование памяти, и настроен для простой поддержки использования библиотек. Далее мы собираемся сделать что-то немного другое: мы собираемся поддерживать отправку уведомлений на рабочем столе о различных событиях в нашем CLI. Для этого в следующей главе мы узнаем о способности Crystal связываться с библиотеками C.

7. Совместимость c C

В этой главе основное внимание будет уделено одной из наиболее продвинутых функций Crystal: возможности взаимодействия с существующими библиотеками C путем написания привязок C. Эта функция Crystal позволяет повторно использовать высокооптимизированный и/или надежный код внутри Crystal, не написав ни строчки C и не беря на себя нетривиальную задачу по переносу всего этого в Crystal. Мы рассмотрим следующие темы:

• Знакомство с привязками C.

• Привязка libnotify

• Интеграция привязок

libnotify позволяет отправлять уведомления на рабочий стол в качестве средства предоставления пользователю ненавязчивой информации при возникновении событий. Мы собираемся использовать эту библиотеку для отправки собственных уведомлений.

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

Технические требования

Требования к этой главе следующие:

• Рабочая установка Кристалла.

• Рабочая установка jq.

• Рабочая установка libnotify.

• Рабочий компилятор C, например GCC.

Инструкции по настройке Crystal можно найти в Главе 1 «Введение в

1 ... 29 30 31 32 33 34 35 36 37 ... 75
Перейти на страницу:

Комментарии
Минимальная длина комментария - 20 знаков. Уважайте себя и других!
Комментариев еще нет. Хотите быть первым?