TIL no.18-병행성(Concurrency)

백선호·2021년 7월 1일
0

TIL

목록 보기
16/39
post-thumbnail

병행성(Concurrency)이란?

병행성이란 한 컴퓨터가 여러 일을 동시에 수행하는 것을 의미한다. 즉 단일 프로그램 안에서 여러 일을 쉽게 해결하는 역할을 하며 독립적으로 진행하는 것처럼 느끼게 해준다. 예를 들어 집에서 영화를 보던 중에 배가 고파서 배달음식을 시키기 위해서 잠시 영화를 멈춰놓고 다시 돌아와서 영화를 다시 보았다. 여기서 중요한 것은 내가 영화를 멈춰 놓은 지점을 기억한다는 것이다. 병해성은 파이썬에서 중요한 장점 중 하나이다. 파이썬 고루틴을 통해 이런 장점을 극대화할 수 있다.

1. 이터레이터(Iterator)와 제너레이터(Generator)

제너레이터는 이터레이터를 생성하는 함수이다. 모든 제너레이터는 이터레이터이다. 이터레이터와 비슷하지만, 제너레이터는 yield 키워드를 사용하고 이터레이터와는 메모리를 적재하는 방식이 다르다.

#파이썬 반복 가능한 타입:for, collections, text file, List, Dict, Set, Tuple, unpacking, *args

1) 이터레이터(Iterator)

t = 'ABCDEFGHIJKLMNOPQRSTUVWXYZ'

w = iter(t) # iter(x) 함수 호출
while True:
    try:
        print(next(w))
    except StopIteration: #StopIteration오류 발생시 break
        break

print()


#A
#B
#C
.
.
.
#Y
#Z

t에 A부터 Z까지 순차적으로 문자를 담은 문자열을 할당하고, w에 iter(t)를 한다. iterable 가능한 객체를 iter() 하게 되면 이터 레이터를 반환된다. try-except 문을 사용해서 이터 레이터가 가장 마지막 요소까지 반환해서 더 이상 반복이 불가능하면 StopIteration 에러를 내고 실행을 종료하게 되는 코드이다. 결과는 A-Z까지 출력된다. 이와 같이 이터 레이터는 다음에 나올 순서를 기억하고 있다.

2) 제너레이터(Generator)

class WordSplitGenerator:
    def __init__(self, text):
        self._text = text.split(' ')
    
    def __iter__(self):
        # print('Called __iter__')
        for word in self._text:
           yield word # 제네레이터
        return
    
    def __repr__(self):
        return 'WordSplit(%s)' % (self._text)


wg = WordSplitGenerator('Do today what you could do tomorrow')

wt = iter(wg)

print(next(wt)) #Do
print(next(wt)) #today
print(next(wt)) #what
print(next(wt)) #you
print(next(wt)) #could
print(next(wt)) #do
print(next(wt)) #tomorrow

제너레이터를 사용해서 이터레이터처럼 동작하는 객체를 만든 것이다. 클래스 WordSplitGenerator 내부에 iter 함수를 만들어 함수 내부에 for 문을 돌려 yiled를 한다. wg에 WordSplitGenerator('Do today what you could do tomorrow')를 할당하고 wt에 iter 함수를 사용한다. print(next(wt))를 통해 단어 하나를 출력한다. 제너레이터는 yield를 통해 중간에 함수 작업을 중단하고 메인 루틴으로 돌아갈 수 있다. 함수 내부에 yield가 포함되어 있다면 제너레이터 취급을 한다.

2. 코루틴(Coroutine)

이터레이터와 제너레이터는 코루틴을 배우기 위한 기초 지식이었다. 어떻게 보면 코루틴은 제너레이터에서 파생되었다고 할 수도 있다. 제너레이터를 사용하여 함수 컨텍스트에서 데이터를 가져올 수 있고 실행을 일시 중지할 수 있다는 개념과 비슷하다. 그렇다고 제너레이터와 코루틴이 같다고 볼 수는 없다.
코루틴은 함수 수만 개를 마치 동시에 실행하는 것처럼 실행하는 효과가 있으며, yileld라는 키워드를 통해서 메인과 서브가 상호작용하여 서로 데이터를 교환한다.

def coroutine1():
    print('>>> coroutine started.')
    i = yield
    print('>>> coroutine received : {}'.format(i))

# 제네레이터 선언
cr1 = coroutine1()


# yield 지점 까지 서브루틴 수행
# next(cr1) # >>> coroutine started.

# 기본 전달 값 None
# next(cr1)   # >>> coroutine started.

# 값 전송 (send에 아무것도 넣지 않으면 기본 전달값 None)
# cr1.send(100)   # >>> coroutine received : 100 StopIteration

coroutine1이라는 함수를 하나 만들었다. i에 yield에 할당했다. next(cr1)으로 yield 전까지 실행했다. 제너레이터 객체. send()를 해서 100을 보내주었다. 이 값이 i로 들어가는 것을 볼 수 있다. 즉 next()로 yield 지점까지 서브루틴을 수행했고 send로 값을 받아서 i에 할당했고 다시 yield 키워드가 있을 때가지 찾아보다 없으니 StopIteration을 발생했다. 결국 send()라는 명령어가 next()의 기능을 갖고 있다. 단 제너레이터 객체를 만들자마자 바로 send를 할 수는 없으며 만들고 최소 1회의 next 다음에 send가 가능하다.

profile
baik9261@gmail.com

0개의 댓글