[golang]goroutineの使い所2 ~ ファイル操作 ~

こんにちは。KOUKIです。Go言語でWeb開発をしてます。

本記事では、業務でよく実装するファイル操作をgoroutineを使った並行処理で実装しています。※記事の前半はgoroutineを使用せず、逐次処理で実装しています。goroutineを使うのは記事の最後になります。

5~10分くらいで読み終わると思いますが、時間がないよって人は、コードサンプルを確認してください。

ファイル操作については、以下の記事も参考になると思います。

ワークスペースの作成

ワークスペースを準備しましょう。

実装

これから作成するプログラムは、terminal上で動きます。

引数から渡されたディレクトリ名からデータを読み取り、ファイルサイズが0のファイル名をtextに出力する簡単なプログラムです。

では、早速実装しましょう。

引数の読み込み

最初に、ターミナルから渡したディレクトリをプログラム側で受け取ります。その為には、osパッケージのArgsメソッドを使用します。

ディレクトリの読み込み

次は、ディレクトリパスの情報を読み込む関数を作成しましょう。

ioutil.ReadDirメソッドにて、ディレクトリパスの情報から格納されているファイルの情報を読み取ることができます。

ファイル情報の出力

読み取ったファイルの情報をTerminal上に表示させてみましょう。

ファイルサイズが0のものだけ、ターミナル上に出力されるプログラムを書きました。

早速、実行してみましょう。

問題なく表示されましたね。

ioutil.ReadDirメソッドで読み取ったデータは、FileInfoインターフェースを持ちます。

これを使って、ファイルの情報にアクセスしています。

ファイル情報の書き込み

Size 0のファイルの情報を出力できるようになったので、新しいファイル(out.txt)にその情報を書き込む処理を実装してみましょう。

上記では、書き込み用関数のwriteFileInfoを実装しました。

書き込みは、ioutil.WriteFileにて行なっています。

情報を書き込むためには、[]byte型のデータを渡す必要があります。

そこで、ワンポイントと呼べる処理が一つあります。

namesは[]byte型ですが、nameはstring型です。本来であれば型が違う為、appendできませんが、省略記号「…」を付与するとstringが分割されたbyteとして格納されるようです。stringは連続したbyte型なんですね。

このプログラムを実行してみましょう。

実行が完了するとout.txtが作成されているはずです。

キャパシティを設定する

out.txtの容量を確認してみましょう。

だいたい65byteです。先ほどまでのプログラムでは、「var names []byte」のようにキャパシティを設定せずにbyteを追加してましたが、プログラミング的には非効率です。

ここもワンポイントなのですが、「キャパシティを設定する方が処理速度が早くなる」ことがあります。

キャパシティを設定するには、make関数が使えます。

キャパシティを測るgetCapSize関数を実装しました。ここで対象のファイル名と「\n」分(+1)のbyte数を計算し、呼び出し元に返します。

そしてこの数値を「names := make([]byte, 0, total)」とすることで、キャパシティを指定することができます。

プログラムを実行してみましょう。

「トータル: 65 bytes.」と出力されているので、out.txtと同じ容量のキャパシティが取得できたことがわかります。

並行処理

これまで実装したプログラムをgoroutineを使って、並列化できないか考えてみましょう。

このプログラムでは、引数に渡したディレクトリ単位で処理を走らせることができます。そのため、ディレクトリ検索から並行化できそうです。

検証のため、ディレクトリとファイルを作成しましょう。

まず、引数ごとにファイルを処理できるようにします。

今までは、files, err := ioutil.ReadDir(args[0])のように渡した引数の最初の値のみ処理をしていましたが、この処理をfor _, dirName := range argsに変更しました。

このようにすることで、渡した引数文処理を走らせることが可能になります。

writeFileInfo関数にディレクトリ名(dirName)を渡し、出力ファイル名としたので、以下のファイルが出力されているはずです。

ファイルの中身を確認すると問題なくファイル名が出力されていることがわかります。

ここまでの処理を並行化します。

並行処理のチップスを次の記事に記載したので、よかったら参考にしてください。

では、早速実装します。

プログラムを実行してみましょう。

処理は問題なく完了し、PUB_/SUB_out.txtも出力できました。

今回のプログラムは規模が小さいのであまり関係ありませんが、ディレクトリの格納ファイルの増加に伴いfiles, err := ioutil.ReadDir(dirName)の読み込み部分がコストになると思ったので、goroutineでこの処理から並行化しています。

おわりに

goroutineの使い所として、本記事のように処理の順番によって結果が変わらないようなプログラムには有効だと思います。

goroutineは本当に便利なので、使いこなしていきたいですね^^

コードサンプル

Go記事まとめ

オススメ書籍