본문 바로가기
GO

[Go] 채널, 동기화 객체, 리플렉션, 동적 파일 생성, 파일 처리, JSON 문서

by 수픽 2020. 8. 8.

UNIT 34 :: 채널 사용하기


: 고루틴끼리 데이터를 주고받고, 실행 흐름을 제어하는 기능

-  모든 타입을 채널로 사용할 수 있음

- 값이 아닌 레퍼런스 타입

- 채널을 사용하기 전엔 반드시 make함수로 공간을 할당해야 함 -> 동기 채널 생성

make(chan 자료형)

 

#고루틴과 채널을 사용하여 두 정수 값을 더하기

 

고루틴과 채널

- var 키워드로 채널 선언&할당 가능

var c chan int
c = make(chan int)

 

- 채널을 매개변수로 받는 함수는 반드시 go 키워드를 사용하여 고루틴으로 실행

go (sum(1,2,c)

 

- 함수에서 채널을 매개변수로 받을 때 사용하는 형식

변수명 chan 자료형

 

- 채널에 값을 보낼 때 <- 연산자 사용

채널 <- 값

 

- 채널에서 값을 가져옴 : 채널에 값에 들어올 대까지 기다린 뒤 값이 들어오면 값을 가져옴

<- 채널

고루틴과 동기 채널의 흐름

1. 동기 채널

: 동기 채널을 활용하면 고루틴의 코드 실행 순서를 제어할 수 있음

 

2. 채널 버퍼링

: 채널의 버퍼가 가득 차면 값을 꺼내서 출력

- 채널에 버퍼를 1개 이상 설정하면 비동기 채널이 생성 -> 보내는 쪽에서 버퍼가 가득 차면 실행을 멈추고 대기하며 받는 쪽에서는 버퍼에 값이 없으면 대기

make(chan 자료형,버퍼 개수)

 

3. range와 close

- 이미 닫힌 채널에 값을 보내면 패닉 발생

- 채널을 닫으면 range 루프 종료 -> close 함수 사용

- 채널이 열려 있고 값이 들어오지 않는다 = range는 실행 x 계속 대기

- 다른 곳에서 채널 값을 보냈다 = 그때부터 range 계속 반복

 

4. 보내기 전용 및 받기 전용 채널

- 보내기 전용

chan<-자료형

- 받기 전용

<-chan 자료형

 

5. 셀렉트 사용하기

: 여러 채널을 손쉽게 사용할 수 있도록 select 분기문 제공

select { case <- 채널: 코드}

 

- select 키워드 뒤에 검사할 변수 따로 지정 x

- close 함수로 채널을 닫았을 때도 case 실행

- default 케이스 지정 가능 (적절한 처리 안하면 cpu코어를 모두 점유)

- 채널에 값을 보낼 수 있음

case 채널 <- 값:코드

- time.After 함수 = 시간 제한 처리 가능

 

결과(생략)

 

 

UNIT 35 :: 동기화 객체 사용하기


- Mutex : 뮤텍스. 상호 배제. 여러 스레드(고루틴)에서 공유되는 데이터를 보호할 때 사용

- RWMutex : 읽기/쓰기 뮤텍스. 읽기와 쓰기 동작을 나누어 잠금을 걸 수 있음

- Cond : 조건 변수. 대기하고 있는 하나의 객체를 깨울 수 도 있고 여러 개를 동시에 깨울 수도 있음

- Once : 특정 함수를 딱 한 번만 실행할 때 사용

- Pool : 멀티 스레드(고루틴)에서 사용할 수 있는 객체 풀. 자주 사용하는 객체를 

- WaitGroup : 고루틴이 모두 끝날 떄까지 기다리는 기능

- Atomic : 원자적 연산. 더 이상 쪼갤 수 없는 연산. 멀티 스레드(고루틴), 멀티 코어 환경에서 안전하게 값을 연산하는 기능

 

1. 뮤텍스

: 여러 고루틴이 공유하는 데이터를 보호할 때 사용

 

# sync 패키지에서 제공하는 뮤텍스 구조체와 함수

sync.Mutex

func (m* Mutex) Lock() //뮤텍스 잠금

func (m *Mutex) Unlock() //뮤텍스 잠금 해제

고루틴을 한 번만 실행하면, 1000이 출력된다. Lock, Unlock함수는 반드시 짝을 맞춰주어야 데드락이 발생하지 않는다. 

 

2. 읽기,쓰기 뮤텍스 

- 읽기 락 : 읽기 락끼리는 서로를 막지 않음. 읽기 시도 중에 값이 바뀌면 안되기에 쓰기 락은 막음

- 쓰기 락 : 읽기, 쓰기 락 모두 막음

sync.RWMutex

func (m* RWMutex) Lock(),func (m *RWMutex) Unlock() //쓰기뮤텍스 잠금, 해제
func (m* RWMutex) RLock(),func (m *RWMutex) RUnlock() //읽기뮤텍스 잠금, 해제

역시 Lock, Unlock함수는 반드시 짝을 맞춰주어야 데드락이 발생하지 않는다. 쓰기 동작이 끝나야 읽기 동작이 시작된다. 읽기 동작끼리는 서로를 막지 않기때문에 항상 동시에 실행된다.

 

3. 조건 변수 사용하기

sync.Cond
func NewCOnd(I Locker)*Cond //조건 변수 생성
func (c* Cond) Wait() //고루틴 실행을 멈추고 대기
func (c* Cond) Signal() //대기하고 있는 고루틴 하나만 깨움
func (c* Cond) Broadcast() //대기하고 있는 모든 고루틴을 깨움

조건 변수는 뮤텍스를 먼저 생성한 뒤 sync.NewCond 함수로 생성한다. 

 

4. 함수를 한 번만 실행 (Once)

sync.Once
func(*Once)Do(f func()) //함수를 한 번만 실행

Once는 sync.Once를 할당한 뒤에 Do 함수로 사용한다. Do 함수에는 실행할 함수 이름을 지정하거나, 클로저 형태로 함수를 지정할 수 있음. 

 

5. 풀 사용하기

: 객체(메모리)를 사용한 후 보관해두었다가 다시 사용하게 해주는 기능

풀은 일종의 캐시. 메모리 할당과 해제 횟수를 줄여 성능을 높이고자 할 때 사용하며, 여러 고루틴에서 동시에 사용할 수 있음

sync.Pool
func(p *Pool)Get()interface() //풀에 보관된 객체를 가져옴
func(p *Pool)Put(x interface{}) //풀에 객체를 보관

new : 객체가 새로 할당된 경우

used : 풀에 보관된 객체를 사용

대부분 풀에 보관된 객체를 사용하는 것을 알 수 있음 -> 풀을 사용하면 메모리를 효율적으로 관리할 수 있음. 단, 수명 주기가 짧은 객체는 풀에 적합하지 않다.

 

6. 대기 그룹 사용하기

: 고루틴이 모두 끝날 때까지 기다릴 때 사용

임시로 대기 : time.Sleep, fmt.Scanln 함수

sync.WaitGroup
func (wg *WaitGroup)Add(delta int) //대기 그룹에 고루틴 개수 추가
func (wg *WaitGroput)Done(); //고루틴이 끝났다는 것을 알려줄 떄 사용
func (wg *WaitGroput)Wait(); //모든 고루틴이 끝날 때까지 기다림

Add 함수에 설정한 값과 Done 함수가 호출되는 횟수는 같지 않으면 패닉이 발생하므로 주의.

Done 함수는 defer와 함께 사용하여 지연 호출로도 사용할 수 있음

 

7. 원자적 연산 사용하기

: 여러 스레드(고루틴), CPU 코어에서 같은 변수(메모리)를 수정할 때 서로 영향을 받지 않고 안전하게 연산할 수 있음.

CPU의 명령어를 직접사용하여 구현되어 있음

 

sync, sync/atomic 패키지 필요

UNIT 36 :: 리플렉션 사용


: 실행 시점에 인터페이스나 구조체 등의 타입 정보를 얻어내거나 결정하는 기능

 

패키지 : reflect

 

reflect.TypeOf 함수 : 일반 자료형, 구조체의 타입을 알 수 있음. 구조체 -> 패키지.구조체 이름

reflect.ValueOf 함수 : 변수 값 정보 reflect.value를 얻어와 타입 정보, 타입 종류, 변수에 저장된 값을 알 수 있음

 

1. 구조체 태그 가져오기

 

#구조체 필드의 태그 저장 형식

`태그명 : "내용"`

 

2. 포인터와 인터페이스의 값 가져오기

주석 처리 = 런타임 에러

 

UNIT 37 :: 동적으로 함수 생성하기


: 리플렉션을 사용하여 동적으로 함수를 만들어 내는 방법

 

reflect.MakeFunc 함수를 사용

 

UNIT 49 :: 파일 처리하기


1. 파일 쓰기

func Create(name string) (file *File,err error) //기존 파일을 열거나 새 파일을 생성
func (f *File)Close()error//열린 파일을 닫음
func (f *File)Write(b []byte)(n int, err error) //파일에 값을 씀. 파일에 쓴 데이터의 길이와 에러 값을 리턴

hello.txt

 

2. 파일 읽기

func Open(name stirng) (file*File,err error) //파일 열기
func(f *File)stat()(fi FileInfo,err error) //파일의 정보를 얻어옴
func (f *File)Read(b []byte)(n int, err error) //파일에서 값을 읽음. 파일에서 읽은 데이터의 길이와 에러 값을 리턴

 

3. 파일 읽기 쓰기

func OpenFile(name string, flag int, perm FileMode)(file *File, err error) // 파일 플래그, 파일 모드를 지정하여 파일 열기
func (f *File)Seek(offset int64, whence int)(ret int64,err error) //파일을 읽거나 쓸 위치로 이동

 

4. ioutil 패키지 사용하기

func WriteFile(filename string, data []byte, perm os.FileMode) error //파일 쓰기, 에러 값을 리턴
func ReadFile(filename string)([]byte,error//파일 읽기, 읽은 데이터(바이트,슬라이스)와 에러 값을 리턴

 

UINT 50 :: 입출력 인터페이스 사용하기


io.Reader, io.Writer 인터페이스 활용

 

1. 파일 처리하기

func NewReader(rd io.Reader) *Reader //io.Reader 인터페이스로 io.Reader 인터페이스 따르는 읽기 인스턴스 생성
func NewWriter(w io.Writer) *Writer //io.Writer 인터페이스로 io.Writer 인터페이스를 따르는 쓰기 인스턴스 생성
func (b *Writer)WriteString(s string)(int,error) //문자열을 버퍼에 저장
func (b *Writer)Flush() error // 버퍼의 데이터를 파일에 저장

 

필요한 패키지는 os,fmt,bufio

 

2. 문자열을 파일로 저장하기

#strings 패키지

func NewReader(s string) *Reader : 문자열로 io.Reader 인터페이스를 따르는 읽기 인스턴스를 생성

#bufio 패키지

func (b *Writer) ReadFrom(r io.Reader)(n int64, err error) : io.Reader의 데이터를 읽어서 io.Writer에 저장

 

3. 문자열을 화면에 출력하기

 

4. 기본 입출력 함수 사용하기

 

5. 읽기, 쓰기 인터페이스를 함께 사용하기

#bufio 패키지
func(b*Reader)ReadLine()(line[]byte,isPrefix bool,err error) //io.Reader에서 문자열 한줄을 읽어서 바이트 슬라이스로 리턴

 

UNIT 51 :: JSON 문서 사용하기


#encoding/json 패키지

func Marshal(v interface{})([]byter,error) //GO 언어 자료형을 json 텍스트로 변환
func Marshallndent(v interface{},prefix,indent string)([]byte,error) //GO 언어 자료형을 JSON 텍스트로 변환하고 사람이 보기 편하도록 들여쓰기를 해줌
func Unmarshal(data []byte,v interface{}) error //json 텍스트를 go 언어 자료형으로 변환

 

json 문서를 읽는 법

키-값 1단계의 단순한 구조

 

1. 구조체 활용

배열,키-배열,키-값 조합

package main

import (
	"encoding/json"
	"fmt"
)

type Author struct {
	Name  string
	Email string
}

type Comment struct {
	Id      uint64
	Author  Author
	Content string
}

type Article struct {
	Id         uint64
	Title      string
	Author     Author
	Content    string
	Recommends []string  //문자열 배열
	Comments   []Comment //Comment 구조체 배열
}

func main() {
	doc := `
	[{
		"Id":1,
		"Title":"Hello,world!",
		"Author": "{
			"Name":"Maria",
			"Email":"maria@example.com"
		},
		"Content":"Hello~:,
		"Recommends":[
			"John",
			"Andrew"
		],
		"Comments": [{
			"id":1,
			"Author":{
				"Name": "Andrew",
				"Email": "andrew@hello.com"
			},
			Content":"Hello Maria"
		}]	
	}]
	`

	var data []Article
	json.Unmarshal([]byte(doc), &data)
	fmt.Println(data)

}

 

2. json 파일 사용

- 데이터를 JSON 파일로 저장

- 게시물 데이터를 저장할 슬라이스를 선언하고 make 함수로 공간 할당

- 데이터를 채워넣은 뒤 json.Marshal 함수를 사용하여 JSON 문서로 변환

- ioutil.WriteFile 함수 사용 -> 변환된 json 문서를 파일로 저장

- ioutile.ReadFile 함수로 json 파일을 읽어옴

- json.Unmarshal 함수를 사용하여 바이트 슬라이스 b에 저장된 값을 가져옴