프로그램의 영역이 커질수록 복잡도가 증가하게 되는데, 이때 프로그래머는 프로그램의 명확성, 테스트 가능성, 효율성을 유지하면서 늘어나는 요구를 만족시켜야 한다. 그 중에서, 가장 어려운 것은 단일 스레드 프로그램을 동시 실행되는 여러 흐름으로 이뤄진 프로그램 바꾸기
#임의의 좌표에 있는 셀의 값을 설정하는 메서드
class Grid:
def __init__(self, height, width):
self.height = height
self.width = width
self.rows = []
for _ in range(self.height):
self.rows.append([EMPTY] * self.width)
def get(self, y,x):
return self.rows[y % self.height][x % self.width]
def set(y,x,state):
self.rows[y % self.height][x % self.width]
def set(self, y, x, state):
self.rows[y % self.height][x % self.width] = state
# 주별 셀 상태를 얻을 방법이 필요하므로 도우미 함수 만들기
def count_neighbors(y,x,get):
n_ = get(y-1,x+0)#북
ne_ = get(y-1,x+1)#북동
e_ = get(y+0,x+1)#동
se_ = get(y+1,x+1)#남동
s_ = get(y+1,x+0)#남
sw = get(y+1,x-1)#남서
w_ = get(y+0,x-1)#북서
neighbor_states = [n_,ne,e_, se,s_,sw,w_,nw]
count = 0
for state in neighbor_states:
if state == ALIVE:
count +=1
return count
#로직 정의
def game_logic(state, neighbors):
if state == ALIVE:
if neighbors < 2:
return EMPTY # 살아 있는 이웃이 너무 적음: 죽음
elif neighbors > 3:
return EMPTY #살아 있는 이웃이 너무 많음 : 죽음
else:
if neighbors ==3:
return ALIVE #생성됨
return state
#각 제너레이션 함수를 호출하여 갱신
#이로 인해서 함수 인터페이스를 사용해서 코드의 결합도를 낮춘다.
def step_cell(y,x,get, set):
state = get(y,x)
neighbors = count_neighbors(y, x, get)
next_state = game_logic(state, neighbors)
set(y,x,next_state)
#모든 셀이 동일하게 작동시키기
def simulate(grid):
next_grid = Grid(grid.height, grid.width)
for y in range(grid.height):
for x in range(grid.width):
step_cell(y,x,grid.get, next_grid.set)
return next_grid
#위의 코드들은 단일 상황에서 대처는 좋지만 어떠한 변화가 생기면 어려워지므로
#확장 기능이 필요
def game_logic(state , neighbors):
#블로킹 I/O를 여기서 수행
data = my_socket.recv(100)
#위와 같은 방식은 전체 프로그램이 느려짐
프로그램이 커지면서 범위와 복잡도가 증가함에 따라 동시에 실행되는 여러 실행 흐름이 필요해지는 경우가 많다.
동시성을 조율하는 가장 일반적인 방법으로는 팬아웃(새로운 동시성 단위들을 만들어냄)과 팬인(기존 동시성 단위들의 실행이 끝나기를 기다림)이 있다.
파이썬은 팬아웃과 팬인을 구현하는 다양한 방법을 제공e텍스트