728x90

이번 C++ 20에서 가장 기대했던 기능이라서 책도보고 인터넷 검색도 계속하면서 공부를 해본 결과 현재로썬 쉽게 사용하기는 힘들 것 같다...

회사 프로젝트에 적용하기에는 위험 부담이 있다... (디버깅이 너무 힘들 것 같다...)

아마도 코루틴을 이용하는 디자인 패턴이 좀 나오면서 정리가 되면 그때 사용하게 되지 않을까...? 하는 생각이 들 정도이다.

 

유니티를 하면서 코루틴이라는 개념을 처음 접했었는데 개념 자체는 크게 다르진 않은 것 같다.

코루틴의 개념은 이 포스팅에서 잘 설명을 해주고 있다.

(https://m.post.naver.com/viewer/postView.naver?volumeNo=33676443&memberNo=559061)

 

C++20 멀티쓰레드 서버에 코루틴 얹기

[BY MastProgs] C++20 에 추가된 코루틴으로 더 쩔게 코딩할 수 있어졌다< 결론부터 말하기 > C++20의...

m.post.naver.com

 

하지만 사용하기 위해서 준비를 많이 해야되는 부분은 불편해보인다.

 

다른 언어와 달리 C++은 기본적으로 Framework이 존재하지 않는다.

항상 사용자가 만들어서 사용을하든, 다른 라이브러리를 사용하든 어쨋든 간에 별도의 Framework을 생성해야만 한다.

그래서 이러한 준비(약속)가 필요하다.

 

가장 기본적으로 필요한 것은 promise_type이다.

struct promise_type
{
    // 코루틴의 객체를 반환한다.
    CoroutineObject get_return_object() { return {}; }
    
    // 코루틴이 실행 전에 멈춰있어야하는지 확인
    std::suspend_never initial_suspend() const noexcept { return {}; }
    
    // 코루틴이 종료 전에 멈춰있어야하는지 확인
    std::suspend_never final_suspend() const noexcept { return {}; }
    
    // co_return에 의해 호출 (코루틴이 무한 루프로 종료되지 않는다면 불필요)
    void return_void() { }
    
    // co_return val에 의해 호출됨
    void return_value(T value) { *_ptr = value; }
    
    // co_yield에 의해 호출
    std::suspend_always yield_value(const T value) { _value = value; return {}; }
    
    // 예외 발생
    void unhandled_exception() { }
};

 

C++에서의 코루틴 promise_type이라는 객체를 만들어서 커스터마이징을 해야되고, 이것을 이용해서 우리가 아는 코루틴의 개념에 맞게 직접 구현해야한다.

 

구현 내용이 너무 길기 때문에 그냥 다른 포스팅으로 대체한다.

https://openmynotepad.tistory.com/65

 

C++20) Coroutine ( 코루틴 ) - 1

프로그램이 실행될 때 불려지도록 만들어진 프로그램의 중심이 되는 코드들을 Main Routine 이라고 하며, Main Routine 이외에 다른 Routine을 모두 SubRoutine 이라고 한다. 그리고 진입 지점을 여러 개 가

openmynotepad.tistory.com

https://openmynotepad.tistory.com/66

 

C++20) Coroutine ( 코루틴 ) - 2

Coroutine 상태가 할당되면, 해당 할당이 힙에서 발생할 수도 있습니다. ( 힙에 발생해야한다고 생각해야하지만, 컴파일러가 최적화 할 수도 있습니다. ) 만약 힙에서 발생하고, 우리가 만든 promise_t

openmynotepad.tistory.com

https://openmynotepad.tistory.com/67

 

C++20) Coroutine ( 코루틴 ) - 3

co_yield 사용하기 co_yield는 Coroutine의 유용한 키워드이며 일부 값을 반환하지만, 코루틴을 종료시키지 않을 때 사용됩니다. 일반적인 사용 방법은 "특정 유형의 Generator ( 또는 발생기 )"를 생성할

openmynotepad.tistory.com

 

728x90

'Basic Programming > C++ 20' 카테고리의 다른 글

C++20 - 지정된 초기화 (Designated Initialization)  (1) 2023.12.17
C++20 - 삼항 비교 연산자(Three-Way Comparison)  (0) 2023.12.17
C++20 - Ranges  (0) 2023.12.10
C++20 - Module  (0) 2023.12.10
C++20 - Concepts  (0) 2023.12.10
728x90

Ranges는 STL에서 제공되던 iterator의 확장 버전처럼 보인다.

Range : STL 컨테이너를 순회할 수 있는 그룹
View : Range에 대해서 적용할 수 있는 연산

C#의 LINQ 문법과 비슷하다.

키워드 

// std::views::all		
// std::ranges::filter_view / std::views::filter (조건 만족하는거 추출)
// std::ranges::transform_view / std::views::transform (각 요소를 변환)
// std::ranges::take_view / std::views::take (n개 요소를 추출)
// std::ranges::take_while_view / std::views::take_while (조건 만족할 때까지 요소 추출)
// std::ranges::drop_view / std::views::drop (n개 요소를 스킵)
// std::ranges::drop_while_view / std::views::drop_while (조건 만족할 때까지 요소 스킵)
// std::ranges::join_view / std::views::join (view 를 병합)
// std::ranges::split_view / std::views::split (split)
// std::ranges::reverse_view / std::views::reverse (역순서로 순회)
// std::ranges::elements_view / std::views::elements (튜플의 n번째 요소를 대상으로 view 생성)
// std::ranges::keys_view / std::views::keys (pair-like value의 첫번째 요소를 대상으로 view 생성)
// std::ranges::values_view / std::views::values (pair-like value의 두번째 요소를 대상으로 view 생성)

 

벡터의 값들 중에 짝수를 추출해서 2를 곱해준다는 것을 코드로 구현한다면

예전 코드

vector<int> v1 = { 1, 2, 3, 4, 5, 6, 7, 8, 9, 10 };

vector<int> v2;
// 짝수를 추출해서
for (int n : v1)
	if (n % 2 == 0)
		v2.push_back(n);
        
// 2를 곱해준다
for (int& n : v2)
	n = n * 2;

view를 이용한 코드

vector<int> v1 = { 1, 2, 3, 4, 5, 6, 7, 8, 9, 10 };

auto results = v1 | std::views::filter([](int n) { return n % 2 == 0; })
                  | std::views::transform([](int n) { return n * 2; });

 

정렬 방법

vector<int> v1 = { 1, 2, 3, 4, 5, 6, 7, 8, 9, 10 };

std::sort(v1.begin(), v1.end()); // 예전 방식
std::ranges::sort(v1);			 // range를 이용한 방식

 

ref - https://openmynotepad.tistory.com/87

 

C++20) Ranges - 1

1장에서는 Ranges의 기본적인 개요와 view를 다룹니다. 2장에서는 Action을 다룹니다. C++20 에서 Actions가 추가되지 않았습니다. ( 추가가 안된 것은 아니고, namespace 가 추가되지 않음 ) 그에 따라, action

openmynotepad.tistory.com

https://openmynotepad.tistory.com/88

 

C++20) Ranges - 2

views::drop 우리가 문자열 트리밍을 한다고 생각해봅시다. 즉, 어떠한 문자열이 들어오면, 선행/후행 공백이 제거된 새 문자열을 구성하려고 합니다. 예를 들어, "\n\t\r Hello World! \n\n" 를 "Hello World!"

openmynotepad.tistory.com

 

728x90

'Basic Programming > C++ 20' 카테고리의 다른 글

C++20 - 지정된 초기화 (Designated Initialization)  (1) 2023.12.17
C++20 - 삼항 비교 연산자(Three-Way Comparison)  (0) 2023.12.17
C++20 - Coroutine  (1) 2023.12.17
C++20 - Module  (0) 2023.12.10
C++20 - Concepts  (0) 2023.12.10
728x90

C++은 다른 라이브러리를 사용하려면 무조건 헤더를 포함(#include <>) 해서 사용해야하지만, 뒤에 나온 언어에서는 그냥 import만해도 해당 기능을 사용할 수 있다.

그래서 ixx를 만든 것 같다.

ixx 파일 생성

 

Module의 장점

1. 모듈은 1번만 import 된다
2. import의 순서에 상관없다
3. 모듈의 이름을 지정할 수 있다
4. 인터페이스와 구현부를 분리 할 필요가 없다


ixx 파일 사용 방법

module; // global module fragment

// 각종 외부 헤더 추가
#include <vector>

// module 시작
export module math;

// import module ...

// submodule
export import math.time;

// 3) namespace를 지정
export namespace math
{
	template <typename T>
	T Add(T a, T b)
	{
		return a + b;
	}
}

 

일반 cpp에서 사용 방법

#include <iostream>
using namespace std;

import math;

int main()
{
	auto addValue = math::Add(1, 2);
}

 

파티션이라는 기능도 제공한다. 이 기능은 ixx하나에 모든 내용을 넣기에는 부담스럽기 때문에 여러개의 파티션(:)으로 나눌 수 있다.

 

MathPartition.ixx

export module MathPartition;

// Partition
export import :MathPartition_1;
export import :MathPartition_2;

 

MathPartition_1.ixx

export module MathPartition:MathPartition_1;

export void MyFunc()
{

}

 

MathPartition_2.ixx

export module MathPartition:MathPartition_2;

export void MyFunc2()
{

}

 

일반 cpp

#include <iostream>
using namespace std;

import math;

import MathPartition;


int main()
{
	auto addValue = math::Add(1, 2);

	MyFunc();
	MyFunc2();
}

 

ref - https://openmynotepad.tistory.com/78

 

C++20) Modules ( 모듈 ) - 1

시작하며 (약간의 역사...) C++20 에서 C++ 라이브러리와 프로그램을 구성 요소화 하는 Module이 등장하였습니다. Module은 기존의 #include 방식인 Translation Unit 방식과는 다른, 별개와 컴파일되는 소스

openmynotepad.tistory.com

https://openmynotepad.tistory.com/79

 

C++20) Modules ( 모듈 ) - 2

Hello, Modules! 1. Module 사용해보기 아직은 visual studio 에서, Compile과정은 비표준입니다. Module을 사용 전, 세팅해주어야 하는게 있습니다. 1. 속성 -> C/C++ -> 언어 에서 C++ 언어 표준을 /std:c++latest로 변

openmynotepad.tistory.com

 

728x90

'Basic Programming > C++ 20' 카테고리의 다른 글

C++20 - 지정된 초기화 (Designated Initialization)  (1) 2023.12.17
C++20 - 삼항 비교 연산자(Three-Way Comparison)  (0) 2023.12.17
C++20 - Coroutine  (1) 2023.12.17
C++20 - Ranges  (0) 2023.12.10
C++20 - Concepts  (0) 2023.12.10
728x90

우리가 템플릿을 사용할 때 어떤 타입에 대한 제한을 두고 싶을 때 아래와 같이 static_assert를 이용했었다.

template<class T>
engine_ptr<T> NewComponent(const std::wstring& newName, const engine_ptr<const T> newOther)
{
    LOG_ENTRY;

    static_assert(std::is_base_of<Component, T>::value, "NewComponent must base of Component");
    engine_ptr<T> component = Entity::NewComponent<T>(newName, this, newOther);
    components_.emplace_back(component);

    return component;
}

static_assert()를 이용할 때에는 함수 구현부에 해당 내용이 들어가야한다.

 

C++ 20에서는 concepts(https://en.cppreference.com/w/cpp/language/constraints)를 이용할 수 있다.

아래와 같이 requires 키워드를 이용하여 해당 타입일 때에만 template가 될 수 있게 할 수 있다.

// 1) Requires Clause(절)
template<typename T>
requires std::integral<T>
void TestConcept1(T number)
{
	cout << number << endl;
}

// 2) Trailing Requires Clause (뒤에 붙는~)
template<typename T>
void TestConcept2(T number) requires std::integral<T>
{
	cout << number << endl;
}

// 3) Constrained Template Parameter (강요된)
template<std::integral T>
void TestConcept3(T number)
{
	cout << number << endl;
}

// 4) Abbreviated Function Template
void TestConcept4(std::integral auto number)
{
	cout << number << endl;
}

 

이것을 응용하면 아래와 같이 커스터마이징을 할 수 있다.

class GameObject
{

};

class Knight : public GameObject
{

};

template<typename T>
requires std::derived_from<T, GameObject>
void TestObj(T* obj)
{

}

template<typename T>
concept MyConcept = !std::is_integral_v<T> && std::derived_from<T, GameObject>;

template<typename T>
concept Addable = requires (T a, T b)
{
	a + b;
}; // a + b가 가능해야 함

template<typename T>
concept Equality = requires(T a, T b)
{
	{ a == b } -> std::convertible_to<bool>;
	{ a != b } -> std::convertible_to<bool>;
};

template<typename T>
concept Integral = std::is_integral_v<T>;

template<typename T>
concept SignedInt = Integral<T> && std::is_signed_v<T>;

 

ref - https://openmynotepad.tistory.com/69

 

C++20) Concepts ( 콘셉트, 개념 ) - 1

Concepts : Generic Programming의 미래 모든 내용은 Bjarne Stroustrup 교수님의 Good_Concepts에서 발췌하였습니다. Concepts 의 사용은 기존의 Unconstrained Template ( 제한되지 않은, 제약 없는 ) 에 비해 Runtime 비용이

openmynotepad.tistory.com

 

https://openmynotepad.tistory.com/66

 

C++20) Coroutine ( 코루틴 ) - 2

Coroutine 상태가 할당되면, 해당 할당이 힙에서 발생할 수도 있습니다. ( 힙에 발생해야한다고 생각해야하지만, 컴파일러가 최적화 할 수도 있습니다. ) 만약 힙에서 발생하고, 우리가 만든 promise_t

openmynotepad.tistory.com

https://openmynotepad.tistory.com/71

 

C++20) Concepts ( 콘셉트, 개념 ) - 3

Concepts: Generic Programming의 미래 모든 내용은 Bjarne Stroustrup 교수님의 Good_Concepts에서 발췌하였습니다. 5. Concepts로 디자인 하기 좋은 Concept는 무엇인가? 이상적으로 Concept는 어떤 영역에서 근본적인 '

openmynotepad.tistory.com

https://openmynotepad.tistory.com/72

 

C++20) Concepts ( 콘셉트, 개념 ) - 4

Concepts: Generic Programming의 미래 모든 내용은 Bjarne Stroustrup 교수님의 Good_Concepts에서 발췌하였습니다. 6. 개념 오버로딩 ( overloading ) generic programming은 다른 타입에 대한 동일한 작업에 동일한 이름을

openmynotepad.tistory.com

 

728x90

'Basic Programming > C++ 20' 카테고리의 다른 글

C++20 - 지정된 초기화 (Designated Initialization)  (1) 2023.12.17
C++20 - 삼항 비교 연산자(Three-Way Comparison)  (0) 2023.12.17
C++20 - Coroutine  (1) 2023.12.17
C++20 - Ranges  (0) 2023.12.10
C++20 - Module  (0) 2023.12.10
728x90

요약

 - 컨텍스트 : 고루틴에 작업을 요청할 때 작업 취소나 작업 시간 등을 설정할 수 있는 작업 명세서 역할

 

새로운 고루틴으로 작업을 시작할 때 일정 시간 동안만 작업을 지시하거나 외부에서 작업을 취소할 때 사용한다

또한 작업 설정에 관한 데이터를 전달할 수도 있다

 

작업 취소가 가능한 컨텍스트

// 작업이 취소될 때까지 1초마다 메시지를 출력하는 고루틴
package main

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

var wg sync.WaitGroup

func main() {
	wg.Add(1)
	ctx, cancel := context.WithCancel(context.Background()) // 1. 컨텍스트 생성
	go PrintEverySecond(ctx)
	time.Sleep(5 * time.Second)
	cancel()

	wg.Wait()
}

func PrintEverySecond(ctx context.Context) {
	tick := time.Tick(time.Second)

	for {
		select {
		case <-ctx.Done():
			wg.Done()
			return
		case <-tick:
			fmt.Println("Tick")
		}
	}
}

// Tick 5번 출력

 

작업 시간을 설정한 컨텍스트

// 일정 시간동안만 작업을 지시할 수 있는 컨텍스트
package main

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

var wg sync.WaitGroup

func main() {
	wg.Add(1)
	ctx, cancel := context.WithTimeout(context.Background(), 3*time.Second) // 1. 컨텍스트 생성
	go PrintEverySecond(ctx)
	time.Sleep(5 * time.Second)
	cancel()

	wg.Wait()
}

func PrintEverySecond(ctx context.Context) {
	tick := time.Tick(time.Second)

	for {
		select {
		case <-ctx.Done():
			wg.Done()
			return
		case <-tick:
			fmt.Println("Tick")
		}
	}
}

// Tick 3번 출력

 

특정 값을 설정한 컨텍스트

// 컨텍스트에 특정 키로 값을 읽어올 수 있도록 설정할 수 있다
package main

import (
	"context"
	"fmt"
	"sync"
)

var wg sync.WaitGroup

func main() {
	wg.Add(1)
	ctx := context.WithValue(context.Background(), "number", 9) // 1. 컨텍스트 생성 (값 추가)
	ctx = context.WithValue(ctx, "float", 5.0)
	ctx = context.WithValue(ctx, "keyword", "Lilly")
	go print(ctx)

	wg.Wait()
}

func print(ctx context.Context) {
	if v := ctx.Value("number"); v != nil { // 2. 컨텍스트에서 값 읽기
		n := v.(int)
		fmt.Println("number:", n*n)
	}

	if v := ctx.Value("float"); v != nil { // 3. 컨텍스트에서 값 읽기
		n := v.(float64)
		fmt.Println("float:", n*n)
	}

	if v := ctx.Value("keyword"); v != nil { // 4. 컨텍스트에서 값 읽기
		n := v.(string)
		fmt.Println("keyword:", n)
	}

	wg.Done()
}

number: 81
float: 25
keyword: Lilly

 

책 참조 : Tucker의 Go언어 프로그래밍

728x90

'Basic Programming > Golang' 카테고리의 다른 글

Golang - 채널 / 셀렉트  (1) 2023.09.16
Golang - 슬라이스 정렬  (1) 2023.07.13
Golang - 슬라이스 요소 삭제/추가  (0) 2023.07.13
Golang - 슬라이스 복제  (0) 2023.07.13
Golang - Slicing  (0) 2023.07.13
728x90

요약

- 채널 : 고루틴 끼리 메시지를 전달하는 메시지 큐

- 셀렉트 : 여러개의 채널을 동시에 기다리는 구문

 

메시지 큐

 - 윈도우 프로그래밍을 하면서 경험했던 윈도우 메시지 큐와 동일한 개념이다

 - 메시지(다양한 정보)를 Queue에 순차적으로 쌓이게 되고, 순차적으로 처리된다

 

채널 인스턴스 생성

                           ┏ 채널 타입               ┏ 메시지 타입
var messages chan string = make(chan string)
       ┗ 채널 인스턴스 변수                  ┗ 채널 키워드

 - make() 를 통해 생성한다

 - 채널 타입은 channel을 의미하는 chan과 메시지 타입을 합쳐서 표현한다

 - chan string은 string 타입 메시지를 전달하는 채널의 타입이다

 

채널에 데이터 넣기

   ┏ 채널 인스턴스   ┏ 넣을 데이터
messages <- "This is a message"
                ┗ 연산자

 

채널에서 데이터 빼기

       ┏ 빼낸 데이터를 담을 변수 ┏ 채널 인스턴스
var msg string <- messages   ┛
                     ┗ 연산자

 

채널에서 데이터를 하나 넣고 빼는 예제

package main
import (
    "fmt"
    "sync"
    "time"
)

func main()
{
    var wg sync.WaitGroup
    ch := make(chan int)    // 채널 생성
    
    wg.Add(1)
    go square(&wg, ch)      // 고루틴 생성
    ch <- 9                 // 채널에 데이터 넣음
    wg.Wait()               // 작업이 완료되때까지 대기
}

func square(wg *sync.WaitGroup, ch chan int)
{
    n := <- ch              // 데이터를 빼옴
                            // 여기서 데이터가 수신될때까지 대기
    
    fmt.Printf("Square: %d \n", n*n) // Output Square: 81
    wg.Done()               // 작업 완료를 알림
}

 

채널의 크기

 - 일반적으로 채널을 생성하면 크기가 0인 채널이 만들어진다. 크기가 0이라는 뜻은 채널에 들어온 데이터를 담아둘 곳이 없다는 얘기가 된다

 - 채널의 크기가 0이면 데이터 수신자가 없으면 데이터 송신자가 대기를 한다

 - 채널의 크기가 존재하면 데이터 송신자는 데이터를 넣고 원래의 동작을 수행한다

 - 채널의 크기가 존재하지만 채널의 크기를 초과한 데이터가 송신되면 담아둘 곳이 없어서 데이터 송신자는 멈추게 된다

 

채널에서 데이터를 가져가지 않아서 프로그램이 멈추는 예제

package main
import "fmt"

func main() {
    ch := make(chan int)       // 크기가 0인 채널 생성
    
    ch <- 9                    // 데이터 수신자가 없기 때문에 여기서 멈춤
    fmt.Println("Never print") // 실행되지 않는다.
}

 

크기를 가지는 채널 생성 예제

package main

import f "fmt"

func main() {

	f.Println("beffered")

	messages := make(chan string, 2) // string 타입의 채널을 만들고 해당 채널의 버퍼는 2이다.
	messages <- "wisoft"
	messages <- "lab"
    
	f.Println(<-messages)
	f.Println(<-messages)
}

make의 두번째 인자에 숫자를 넣어주면 그 크기로 생성된다

 

채널의 크기를 넘어가는 경우엔?

package main

import f "fmt"

func main() {

	f.Println("beffered")

	messages := make(chan string, 2)
	messages <- "wisoft"
	messages <- "lab"
	messages <- "golang"             // 여기서 deadlock 발생
	f.Println(<-messages)
	f.Println(<-messages)
	f.Println(<-messages)
}

 

------------ 아래 부터는 영역을 나누는 방법 ------------

 

채널에서 데이터 대기

package main

import (
	"fmt"
	"sync"
)

func square(wg *sync.WaitGroup, ch chan int) {
	for n := range ch {
		fmt.Println("Square: %d \n", n*n)// 2. 데이터를 계속 기다림
	}

	wg.Done()                            // 4. 실행되지 않음
}

func main() {
	var wg sync.WaitGroup
	ch := make(chan int)

	wg.Add(1)
	go square(&wg, ch)

	for i := 0; i < 10; i++ {
		ch <- i * 2                      // 1. 데이터 송신
	}

	wg.Wait()                            // 3. 작업 완료를 기다림
}

위에 코드에서는 for range 구문에서 계속해서 데이터가 들어오기를 기다리기 때문에 4번이 실행되지 않고 deadlock

 

채널 닫기

package main

import (
	"fmt"
	"sync"
)

func square(wg *sync.WaitGroup, ch chan int) {
	for n := range ch {
		fmt.Println("Square: %d \n", n*n)
	}

	wg.Done()                            
}

func main() {
	var wg sync.WaitGroup
	ch := make(chan int)

	wg.Add(1)
	go square(&wg, ch)

	for i := 0; i < 10; i++ {
		ch <- i * 2                      
	}
    
    close(ch)                            // 채널 닫음

	wg.Wait()                            
}

데이터를 모두 넣고 채널을 닫아주면 for range에서는 채널이 닫혀서 더이상 수신을 할 수 없기 때문에 for문을 빠져나오고 정상 종료된다

 

Select 문

Select문은 여러 채널을 동시에 기다릴 수 있다. 하지만 어떤 채널에서라도 데이터를 읽어오면 해당 구문을 실행하고 select문은 종료된다. 하나의 case만 처리되면 종료되기 때문에 반복해서 데이터를 처리하고 싶다면 for문과 함께 사용해야 한다

select {
case n := <-ch1
    ...  // ch1 채널에서 데이터를 빼낼 수 있을 때 실행
case n := <-ch2
    ...  // ch2 채널에서 데이터를 빼낼 수 있을 때 실행
case ...
}

 

select를 이용한 데이터 읽고 처리하는 예제

package main

import (
	"fmt"
	"sync"
)

func square(wg *sync.WaitGroup, ch chan int, quit chan bool) {
	for {
		select { // ch와 quit 양쪽을 모두 기다림 (non blocking)
		case n := <-ch:
			fmt.Println("Square: %d \n", n*n)
		case <-quit:
			wg.Done()
			return
		default:
			fmt.Println("test") // 실행되지 않음
		}
	}
}

func main() {
	var wg sync.WaitGroup
	ch := make(chan int)
	quit := make(chan bool)

	wg.Add(1)
	go square(&wg, ch, quit)

	for i := 0; i < 10; i++ {
		ch <- i * 2
	}

	quit <- true
	wg.Wait()
}

 

------------ 아래 부터는 역할을 나누는 방법 ------------

자동차 공장에서 자동차를 만드는 예제

package main

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

type Car struct {
	Body  string
	Tire  string
	Color string
}

var wg sync.WaitGroup
var startTime = time.Now()

func main() {
	tireCh := make(chan *Car)
	paintCh := make(chan *Car)

	fmt.Printf("Start Factory \n")

	wg.Add(3)
	go MakeBody(tireCh) // 1. 고루틴 생성
	go InstallTire(tireCh, paintCh)
	go PaintCar(paintCh)

	wg.Wait()
	fmt.Println("Close the factory")
}

func MakeBody(tireCh chan *Car) { // 2. 차체 생산
	tick := time.Tick(time.Second)
	after := time.After(10 * time.Second)

	for {
		select {
		case <-tick:
			// Make a Body
			car := &Car{}
			car.Body = "Sports car"
			tireCh <- car
		case <-after: // 3. 10초 뒤 종료
			close(tireCh)
			wg.Done()
			return
		}
	}
}

func InstallTire(tireCh chan *Car, paintCh chan *Car) { // 4. 바퀴 설치
	for car := range tireCh {
		// Make Tire
		time.Sleep(time.Second)
		car.Tire = "Winter tire"
		paintCh <- car
	}

	wg.Done()
	close(paintCh)
}

func PaintCar(paintCh chan *Car) { // 5. 도색
	for car := range paintCh {
		// Make a body
		time.Sleep(time.Second)
		car.Color = "Red"
		duration := time.Now().Sub(startTime) // 6. 경과 시간 출력
		fmt.Printf("%.2f Complete Car: %s %s %s\n", 
                    duration.Seconds(), car.Body, car.Tire, car.Color)
	}

	wg.Done()
}

 

 

 

 

책 참조 : Tucker의 Go언어 프로그래밍

사이트 참조 (고루틴/채널/동기화/셀렉트) : https://judo0179.tistory.com/88

사이트 참조 (c++ select) : https://reakwon.tistory.com/117

 

[리눅스] 다중입출력 - select개념과 설명과 예제

select, poll, epoll과 같이 더 많은 정보와 예제를 담은 리눅스 교재를 배포했습니다. 아래의 페이지에서 리눅스 교재를 받아가세요. https://reakwon.tistory.com/233 리눅스 프로그래밍 note 배포 티스토리에

reakwon.tistory.com

 

728x90

'Basic Programming > Golang' 카테고리의 다른 글

Golang - 컨텍스트  (0) 2023.09.16
Golang - 슬라이스 정렬  (1) 2023.07.13
Golang - 슬라이스 요소 삭제/추가  (0) 2023.07.13
Golang - 슬라이스 복제  (0) 2023.07.13
Golang - Slicing  (0) 2023.07.13
728x90

int 슬라이스 정렬

import(
       "fmt"
       "sort"
)

func main()
{
    s := []int{5,2,6,3,1,4}
    sort.Ints(s) // 정렬 끝 [1,2,3,4,5,6]
}

 

구조체 슬라이스 정렬

import(
       "fmt"
       "sort"
)

type Student struct
{
    Name string
    Age int
}

type Students []Student

func (s Students) Len() int { return len(s) }
func (s Students) Less(i, j int) bool { return s[i].Age < s[j].Age }
func (s Students) Swap(i, j int) { s[i], s[j] = s[j], s[i] }

func main()
{
    s := []Student
    {
        {"가", 31},
        {"나", 52},
        {"다", 42},
        {"라", 38},
        {"마", 20}
    }
    
    sort.Sort(Students(s))
}

 

728x90

'Basic Programming > Golang' 카테고리의 다른 글

Golang - 컨텍스트  (0) 2023.09.16
Golang - 채널 / 셀렉트  (1) 2023.09.16
Golang - 슬라이스 요소 삭제/추가  (0) 2023.07.13
Golang - 슬라이스 복제  (0) 2023.07.13
Golang - Slicing  (0) 2023.07.13
728x90

슬라이스 요소 삭제

slice1 := []int{1, 2, 3, 4, 5}
idx := 2 // 삭제할 인덱스

for i := idx + 1; i < len(slice); i++
{
    slice2[i - 1] = slice[i]    // slice1의 모든 값을 앞으로 당기기
}

slice = slice[:len(slice)-1]    // 마지막 값을 삭제
                                // [1, 2, 4, 5, 6]

 

append를 이용한 슬라이스 요소 삭제

slice1 := []int{1, 2, 3, 4, 5}
idx := 2

// append를 이용한 slice1 복사
slice2 := append(slice1[:idx], slice1[idx+1:])

// slice1[:idx]는 [1,2]
// slice1[idx+1:]는 [4,5,6]
// 두개를 합치면 [1,2,4,5,6]

 

슬라이스 요소 추가

slice := []int{1, 2, 3, 4, 5, 6}
slice = append(slice, 0) // 맨 뒤에 요소 추가

idx := 2 // 추가하려는 위치

for i := len(slice)-2; i <= idx; i--
{
    slice[i+1] = slice[i] // 맨 뒤부터 추가하려는 위치까지 값을 하나씩 밀기
}

slice[idx] = 100 // 값 변경

 

append를 이용한 슬라이스 요소 추가

slice := []int{1, 2, 3, 4, 5, 6}
idx := 2

// append를 이용한 slice1 복사
slice = append(slice[:idx], append([]int{100}, slice[idx:]))

// slice[:idx]는 처음부터 삽입하는 위치까지의 슬라이스 [1, 2]
// []int{100}은 삽입하려는 값으로 10 한개만 갖는 슬라이스
// slice[idx:]는 삽입하려는 위치부터 끝까지의 슬라이스 [3,4,5,6]
// append함수로 []int{100}에 slice[idx:]를 합쳐서 [100, 3, 4, 5, 6]
// append함수로[1, 2]에 [100, 3, 4, 5, 6]를 합쳐서 [1, 2, 100, 3, 4, 5, 6]

 

copy로 요소 추가

slice := []int{1, 2, 3, 4, 5, 6}
idx := 2

slice = append(slice, 0) // 맨 뒤에 요소 추가
copy(slice[idx+1:], slice[idx:]) // 값 복사
slice[idx] = 100 // 값 변경

 

- 출처 : Tucker의 Go 언어 프로그래밍

728x90

'Basic Programming > Golang' 카테고리의 다른 글

Golang - 채널 / 셀렉트  (1) 2023.09.16
Golang - 슬라이스 정렬  (1) 2023.07.13
Golang - 슬라이스 복제  (0) 2023.07.13
Golang - Slicing  (0) 2023.07.13
Golang - Slice append()를 사용할 때 발생하는 문제  (0) 2023.07.12

+ Recent posts