Diary

Diary

日々学んだことをアウトプットする場として初めてみました

WaitGroup

WaitGroupについて

syncパッケージ

syncパッケージには低水準のメモリアクセス同期に便利な並行処理のプリミティブが入っている

WaitGroup

ひとまとまりの平行処理があったとき、その結果を気にしない、もしくは他に結果を集約する手段があるときに、それらの処理の完了を待つ手段として非常に強力

つまりどの順番で終わるかは確約されてないってこと???

使い方

  1. Addに引数を渡して呼び出し、その数だけのゴルーチンが起動したことを表す
  2. Doneをdeferキーワードを使って呼び出し、ゴルーチンのクロージャーが終了する前にWaitGroupを確実に終了する
  3. Waitを呼び出すことで、この時点ですべてのゴルーチンが終了するまでメインゴルーチンをロックする
package main

import (
    "fmt"
    "sync"
    "time"
)

func main() {
    var wg sync.WaitGroup

    wg.Add(1)
    go func() {
        defer wg.Done()
        fmt.Println("1st goroutine sleeping...")
        // time.Sleep(1*time.Second)
        time.Sleep(1)
    }()
 
    wg.Add(1)
    go func() {
        defer wg.Done()
        fmt.Println("2nd goroutine sleeping...")
        // time.Sleep(2*time.Second)
        time.Sleep(1)
    }()
 
    wg.Wait()
    fmt.Println("All goroutines complete.")
}

// 出力結果
// 2nd goroutine sleeping...
// 1st goroutine sleeping...
// All goroutines complete.

このプログラムの出力結果は上のようになる。 何回かに一回は、1st → 2nd の順番になる。

(やっぱりこれは順番までは確保してくれないよ〜〜ってことの表れなのか??)

順番を気にしない平行処理に向いている

並行処理入門

Goにおける並行処理入門

この記事では、Goにおける並行処理を実現するための準備。

次回以降、実際にコードを書いて確かめていきたい。

WHY 並行処理

ムーアの法則が破綻し限界を迎えるにつれ、単純に従来のやり方ではスピードを上げ続けることができなくなった。

その流れでマルチコアプロセッサーが登場し、これをうまく利用することが必要となった。そのための処理が並行処理である 。

並行処理と並列処理

並行処理と並列処理はごっちゃにされることが多いが、分けて考える必要がある。

並行処理

コンテキストスイッチを切り替えながら、複数の処理を「論理的に」実行すること

目的は「他を待たせないこと」

並列処理

並行処理の一部

マルチコアの利用による計算処理の高速化などが目的。(πの値の計算におけるspigotアルゴリズムなど)

また、次のような言葉もある

並行性はコードの性質を指し、並列性は動作しているプログラムの性質を指す。つまり、並列性はプログラムのランタイムの性質でありCPUのコアなどに依存する。

非同期処理

非同期処理は、並列or並行のどちらの処理を利用しても実現可能

分散処理

次のような方法が考えられる

  • スケールアップ(マシンの処理性能アップ)
  • スケールダウン(サーバーの数を増やす)

マルチプロセス

プロセス内で子プロセスを生成する

ただ、次のような問題点が生じやすい

  • プロセスの生成は、オーバーヘッドが大きい
  • プロセス間通信も、オーバーヘッドが大きい
  • 並行処理固有のバグが生じやすい

コルーチン(yield, async/await)

  • OSのファイバーを使って実現
  • メリット
    • メモリ空間が同じため、coroutine間で同じデータを共有可能
    • 切り替えのタイミングをプログラムの中で制御できる
  • デメリット
    • 並行処理はできるが並列処理は無理
    • マルチコアを生かすには、スレッドを利用する必要がある

ゴルーチン

  • メリット
    • カーネルスレッドの数が膨れ上がることがない(C10K問題を回避)
    • スレッドによりタスクの切り替えを行う
    • 複数のOSスレッド上にマッピングして同時に実行されるため、並列でも動作する

並行処理の難しさ

  • データ競合(data race)
    • 読み込み中に書き込んでしまうこと
  • 原子性
    • 特定の文脈下で分割不能
  • デッドロック
    • 全ての並行なプロセスが、お互いの処理を待ち合っている状態
  • ライブロック
    • 並行処理を行なっているけれど、その操作はプログラムの状態を全く進めていない
  • リソース枯渇

文字コードをターミナルで調べる

文字コードをターミナルで調べる

  • fileコマンド
  • od, xddコマンドなどを使用する

ファイルがどの文字コードを使用してるかを調べる

$ file hoge 
hoge: ASCII text
$ file a
a: UTF-8 Unicode text

od

文字列を16進数へと変換してくれる

$ od -tx1 FILENAME
0000000    e7  bd  ae  e6  8f  9b  e3  82  b3  e3  83  9e  e3  83  b3  e3
0000020    83  89  e7  b7  a8  0a  0a  20  0a  74  72  3a  20  e7  bd  ae
0000040    e6  8f  9b  e3  81  99  e3  82  8b  e3  81  a0  e3  81  91  e3
0000060    81  8b  e3  81  a8  e6  80  9d  e3  81  a3  e3  81  a6  e3  81
0000100    84  e3  81  9f  e3  81  91  e3  81  a9  e3  80  81  e6  8c  99
0000120    e5  8b  95  e3  81  8c  e3  81  a1  e3  82  87  e3  81  a3  e3
0000140    81  a8  e6  80  9d  e3  81  a3  e3  81  9f  e3  81  ae  e3  81
0000160    a8  0a

最後の0a(=0x0a)linux,macにおける改行コード(LF)を表す

横の行数表示も16進数にしたければ、-Axをつける

$ echo hogehogehogeあお | od -tx1 -Ax
0000000    68  6f  67  65  68  6f  67  65  68  6f  67  65  e3  81  82  e3
0000010    81  8a  0a                                                    
0000013

ASCII以外の文字は、URLエンコードと同じようにUTF-8で帰ってくるのか

xxd

odと同じように、16進数ダンプしてくれるツール

$ xxd hoge
00000000: 5468 6973 2069 7320 6120 7065 6e2e 0a54  This is a pen..T
00000010: 6869 7320 6973 2074 6865 2070 656e 2e0a  his is the pen..
00000020: 5468 6973 2069 7320 6161 2070 656e 2e0a  This is aa pen..
00000030: 5468 6573 6520 6172 6520 7065 6e73 2e0a  These are pens..
00000040: 5468 6573 6520 6973 2061 2070 656e 2e0a  These is a pen..
00000050: 5468 6973 2069 7320 7468 6520 7065 6e73  This is the pens
00000060: 2e0a 5468 6973 2069 7320 6120 7065 6e2e  ..This is a pen.
00000070: 0a 

echo

echo -eUnicode文字列を表示できる

$ echo hoge | xxd
00000000: 686f 6765 0a                             hoge.
$ echo -e "\u68\u6f\u67\u65"
hoge

手を動かすことで少しでも文字コードと仲良くなれたら幸いです。

文字コードど入門

文字コード入門

文字コードについて全くの初心者が、少し調べたのでまとめてみた。

  • 調べる前の自分の知識
    • ASCIIってのは文字関係のなんかでしょ
    • Unicode,utf-8って文字関係の...
    • で、どう違うん???

文字をコンピュータが扱う

  • 文字だけに限らないが、コンピュータが扱う際には全て0・1の2進数で扱う必要がある。

符号化

  • この0・1の2択のことを文字を扱う世界では”符号”と呼んでおり、文字を0・1の符号に変換することを符号化という。

文字コード

  • 符号化の際のルールのこと。符号化文字集合とも呼ばれる

文字化け

  • 符号化の解釈を異なる文字コードで実行しようとするときに発生する

文字コードの例

ASCII(アスキー

  • ”American Standard Code for Information Interchange”の略
  • American とあるように、ラテン文字の取り扱いがメイン
  • 7ビットの1バイトコード
    • ラテン語アルファベット26*2(大文字小文字)
    • 数字10(0−9)
    • 記号
    • を含めても128パターン(7ビット)で足りる

JIS X 0208

  • 日本語の最も基本的な2バイト文字列
  • 2バイトで94*94=8836文字登録できる
    • 94=128(7ビット)ー32(制御文字)ー2(なんだっけ)

Unicode

  • 世界中の文字を1つの文字集合へと収めるべく開発されたもの
  • 元々の思想は16ビット(2バイト)で全てを表現すること!(UTF-16
    • 足りるわけない!!(Unicodeの失敗)
    • ASCIIとバイト単位で互換可能なものが UTF-8 で、3バイトある

制御文字

  • 制御文字とは、普通の意味の文字ではないもの。例えば改行コードなど
  • ASCIIでは、0x00~0x1Fまでが割り当てられている。
  • よく使われる制御文字
制御文字 ASCII
ベル 0x07
水平タブ 0x0B
CR 0x0D
LF 0x0A

zshrcやzshprofileとその違い

zshrcやzshprofileとその違い

どちらもzshの設定ファイルであるが、呼び出される順番や使用シェルによって呼び出されるかどうかが若干違う

設定ファイル一覧

システム全体用 ユーザーごと
共通 ①/etc/zshenv ②~/.zshenv
ログインシェルの場合 ③/etc/zprofile ④~/.zprofile
インタラクティブシェルの場合 ⑤/etc/zshrc ⑥~/.zsshrc
ログインシェルの場合 ⑦/etc/zlogin ⑧~/.zlogin
ログアウト時 ⑨/etc/zlogout ⑩~/.zlogout
  • !①や②はシェルスクリプトの動作に影響を与える可能性があるため、注意!
  • 実際に実行されるのは?
    • ログインシェル:①〜⑧が順番に実行される
    • ログイン以外のインタラクティグシェル:①②⑤⑥が順番に実行される
    • ノンインタラクティグシェル:①②が順番に実行される
    • ログアウト時:⑨⑩が実行される

ログインシェルとは?

基本的に普通に開いたものはログインシェルだと思う

確認方法

  • $SHELLで確認する

環境変数$SHELLに保存されているものがログインシェル

$ echo $SHELL
/bin/zsh

であれば、zshがログインシェル

  • $0で確認する
$ echo $0
-zsh

のように、-(ハイフン)付きで表示されてたらログインシェル

以下のように、新たにシェルを立ち上げるとログインシェルではなくなり、ハイフンはなくなる

$ zsh
Restored session: 2021年 6月19日 土曜日 19時58分52秒 JST
$ echo $0
zsh
$ exit
Saving session...
...saving history...truncating history files...
...completed.

Vimiumの便利な使い方

Vimiumとは何か

  • 便利なChrome拡張機能
  • Vimの操作の感覚でブラウザを操作できる
  • 一応成人向けコンテンツらしい

f:id:kokoichi206:20210619144134p:plain

Vimiumについて

簡単な使い方

できること Key
ページトップへ gg
ページ下部へ G
定量下へ d
定量上へ u
前のページへ H
次ページへ L
ブックマーク・履歴検索 o
ページ内リンク一覧正直 f
ソースを確認 gs

検索におけるVimium

以下の使い方は同じくChrome拡張機能である検索キーボードショートカットを用いている

検索キーボードショートカットは「検索時に検索欄を方向キーとエンターで行き来できる優れもの」なのだが、Vimiumと組み合わせるとさらに便利

  1. iを押してインサートモードに入る
  2. j,kが方向キーの役割を持つので、それを押すことでページリンクを移動
  3. l,hで次10件の検索や前10件の検索にも飛べる

これを知ってからは検索すると秒でijを押すようにしてる

SQLBoilerで null.String を扱う

SQLBoilerで null.String を扱う

SQLBoilerを使っていて null.String 型の定義のフィールドにアクセスするのに苦労したのでメモしておく

SQLBoilerとは

  • SQLBoilerはデータベースファーストのORマッパーである
    • あらかじめDBを生成しておくと、その定義に基づいて構造体を自動生成してくれる

自動生成してくれる構造体の例

type Room struct {
    RoomID        int         `boil:"room_id" json:"room_id" toml:"room_id" yaml:"room_id"`
    RoomName      null.String `boil:"room_name" json:"room_name,omitempty" toml:"room_name" yaml:"room_name,omitempty"`
    IsGroup       null.Bool   `boil:"is_group" json:"is_group,omitempty" toml:"is_group" yaml:"is_group,omitempty"`
    LatestContent null.String `boil:"latest_content" json:"latest_content,omitempty" toml:"latest_content" yaml:"latest_content,omitempty"`
    CreatedAt     null.Time   `boil:"created_at" json:"created_at,omitempty" toml:"created_at" yaml:"created_at,omitempty"`
    UpdatedAt     null.Time   `boil:"updated_at" json:"updated_at,omitempty" toml:"updated_at" yaml:"updated_at,omitempty"`

    R *roomR `boil:"-" json:"-" toml:"-" yaml:"-"`
    L roomL  `boil:"-" json:"-" toml:"-" yaml:"-"`
}

null.String?

基本的には、INSERTなどに使える関数も含め全て自動生成してくれたファイルに書いてある

使い方としては例えば次のようになる

u := &models.User{Name: name, Password: pwHash, Email: email}
err := u.Insert(Ctx, DB, boil.Infer())

ところが、上で紹介したRoomの構造体に現れるnull.Stringなどの「null+何か」みたいなものに対し、普通にstringと思ってやろうとするとエラーになる

{
  ...
  room, _ := models.FindRoom(Ctx, DB, room_id)
  room.LatestContent = content
  fmt.Println(room)
  _, err := room.Update(Ctx, DB, boil.Infer())
  ...
}
> cannot use content (type string) as type null.String in assignment

どうやらこのnull.Stringは1つの構造体らしい

試しに構造体の定義から、null.Stringに飛んでみると

// String is a nullable string. It supports SQL and JSON serialization.
type String struct {
    String string
    Valid  bool
}

と定義があった(null/v8の中)。

そうと分かれば、構造体の中のStringフィールドにアクセスしたら良さそう

{
  ...
    room, _ := models.FindRoom(Ctx, DB, room_id)
    room.LatestContent.String = content
    _, err := room.Update(Ctx, DB, boil.Infer())
  ...
}

エラーは出ないけど、DBに反映されない!

roomを1回出力してみる

{
  ...
    room, _ := models.FindRoom(Ctx, DB, room_id)
    room.LatestContent.String = content
         fmt.Println(room)
    _, err := room.Update(Ctx, DB, boil.Infer())
  ...
}
> &{3 {Group1 true} {false true} {content false} {2021-06-06 20:46:48.478066 +0000 +0000 true} {2021-06-06 22:49:19.573172 +0000 +0000 true} <nil> {}}

このなかの3つ目くらいの{content false}が変えたい部分なのだが、falsetrueに変える必要があるっぽいな

これは、null.Stringの定義のValidに当たる部分だと思う

{
  ...
    room, _ := models.FindRoom(Ctx, DB, room_id)
    room.LatestContent.String = content
  room.LatestContent.Valid = true
    _, err := room.Update(Ctx, DB, boil.Infer())
  ...
}

これでようやくDBにうまく反映されるようになった