LoginSignup
9
8

More than 5 years have passed since last update.

golangとは何なのか調べてみた

Last updated at Posted at 2019-02-05

半年ほど前から何となくgolangを書き始めてみた。
業務ではjavaばかり書いていたので、気分転換にと思って書き始めたが、どうせならしっかり言語特性を理解したいなと思い、golangについて調べてみた。

【参考にしたサイト】
golang公式(FAQ)
Go言語らしさとは何か?を題材にした記事
golangの並列処理について理解しやすい記事

golangが作られた背景

  • 動的型付け言語のプログラミング容易性、静的型付け言語の安全性・効率的なコンパイルを一つの言語で享受したい。
  • マルチコアプロセッサを生かした優れた非同期処理による実行効率を目指した。
  • 本当に開発者が必要だと思う機能のみを提供するように、言語仕様をリッチにしないことにより、開発者の迷いを無くし、生産性の高い開発をサポートしたい。

golangの強力な機能

interface

  • golanginterfaceinterfaceに定義したメソッドを実装しただけでinterfaceを実装していると見なされる。
  • これはinterfaceを「ある概念を表すための振る舞いを定義するもの」としてではなく「概念を扱う側がどう扱いたいかを定義するもの」として扱えるということだと理解。
  • 「依存性逆転の法則」を正確に、コンパクトに実現できるようになっている。
main.go
package main

import (
    "fmt"
    "strconv"
)

// メイン処理
func main() {
    PrintGame(Soccer{11, "サッカー", 120})
    PrintGame(Chess{9, "チェス", 9})
}

func PrintGame(g Game) {
    fmt.Println(g.getGameName() + "は" + strconv.Itoa(g.getPlayerMemberCount()) + "人で行うゲームです")
}

// ゲームインターフェース(メイン処理で使いたいもののみを定義)
type Game interface {
    getPlayerMemberCount() int
    getGameName() string
}

// サッカー構造体
type Soccer struct {
    playerMemberCount int
    gameName          string
    playTime          int
}

func (s Soccer) getPlayerMemberCount() int {
    return s.playerMemberCount
}

func (s Soccer) getGameName() string {
    return s.gameName
}

// チェス構造体
type Chess struct {
    playerMemberCount int
    gameName          string
    boardSize         int
}

func (c Chess) getPlayerMemberCount() int {
    return c.playerMemberCount
}

func (c Chess) getGameName() string {
    return c.gameName
}

func (c Chess) getBoardSize() int {
    return c.boardSize
}

コンポジション

  • 継承という概念を無くし、オブジェクト志向言語として扱いたい場合に、継承ではなく移譲を強制し、コードの複雑さを取り払う。
  • 個人的には多重継承された実装は可読性が低く、スパゲティコードの温床になり得ると思っているので、この仕様は好み。
main.go
package main

import (
    "fmt"
    "time"
)

// メイン処理
func main() {
    order := Order{"パン", OrderDetail{100, "buy", time.Now()}}
    fmt.Println(order.getItemName())
    // 注文に埋め込んだ構造体のメソッドを直接呼び出せる
    fmt.Println(order.getOrderPrice())
    fmt.Println(order.getBuySellType())
    fmt.Println(order.getOrderDateTime())
}

// 注文構造体
type Order struct {
    itemName string
    // 注文明細を注文に埋め込み
    OrderDetail
}

func (o Order) getItemName() string {
    return o.itemName
}

// 注文明細構造体
type OrderDetail struct {
    orderPrice    int
    buySellType   string
    orderDateTime time.Time
}

func (d OrderDetail) getOrderPrice() int {
    return d.orderPrice
}

func (d OrderDetail) getBuySellType() string {
    return d.buySellType
}

func (d OrderDetail) getOrderDateTime() time.Time {
    return d.orderDateTime
}

非同期処理

golangには並行処理を行える機能としてgoroutineが実装されています。
goroutineは「並列処理」ではなく「並行処理」を行う機能です。(自分も曖昧だった概念のためこの機にちゃんと理解した。。)

goroutineが他の言語の並行処理に比べて優れていると言われている点は二つ。

  • 並行処理の確保するメモリが軽量
  • 並行処理の実行速度

この二点について調べてみました。

並行処理の確保するメモリが軽量

一つのgoroutineが起動時に確保するメモリ容量と、メモリ確保の方法に起因しています。
一般に、スレッドを複数起動して処理を行う場合、一つのスレッドを起動するにあたり、プロセス内のメモリ領域がスレッド同士で干渉し合わないように大きめにメモリ領域(1MBくらいらしい)を確保します。

それに比べてgoroutineはスタック領域に2KBほどを確保するのみだそうです。そこからgoroutine として動作する関数の必要とするメモリのみをヒープ領域に割り当てていく仕組みになっている。

そのため、何千何万とスレッドを立てるには膨大なメモリが必要になりますが、goroutineであればそんな心配はしなくて良いということ。だと思う。

並行処理の実行速度

これはスレッドがコンテキストスイッチを行う時に比べて、goroutineの行うコンテキストスイッチの方がコストが少ないということに起因している。
スレッドであればCPUの複数のレジスタから複数のレジスタにスレッドの扱っているデータを退避させるという作業が発生しますが、goroutineであればせいぜい3,4種類のレジスタの退避を行えば良いだけなのです。

また、goroutineのスケジューラはgoroutineのブロッキング処理を検知すると別の goroutineの処理を実行し始めるため、システムリソースを無駄にしないよう設計されています。

最後に

ここまでgolangの機能や設計思想について調べた結果を記載しましたが、調べる中で、かなりスリム化された言語であると感じました。
業務ではjavaを使用していますが、色々便利な機能があったり、汎用的に実装するための機能が提供されています。
が、私を含め多くのメンバーがjavaのそういった機能を使いこなせているかというとそうではないと感じています。
それどころか、そういった機能を使うことにより保守しにくいコードを量産してしまっているのでは。。

そのため、golangのように面白みがない言語だと揶揄されることがあっても、保守しやすいコードを効率的に書くことをある程度強制するような言語に触れることは、学びになると思いました。
これからもgolangを趣味ではありますが、書いていこうと思います。

9
8
0

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
9
8