안녕하세요, 강사님.
항상 좋은강의에 감사드립니다.
섹션2을 다 듣고나서, 스스로 정리를 하고 궁금한 부분들을 한번에 질문드립니다.
제가 컴퓨터쪽 전공이 아니라서 용어나 잘 모르는 부분이 많아 질문이 깔끔하지 않은점 이해 부탁드립니다.
(섹션1도 다 듣고 동일한 방법으로 질문을 최근에 해서... 혹시 게시판에 도배가 되지 않을까 실례됩니다. 너무 질문이 잦다면 말씀주세요!)
그리고 제 질문들이 당연한것일수도 있는데... 강사님께 한번 더 확인(?)을 받으면 더 효율적으로 이해가되서 문의드립니다^^!
질문이 많고 clear하지 않음에 다시한번 양해를 부탁드리며, 미리 답변에 진심으로 감사드립니다.
0. thread와 process 관련 high-level로 만들어놓은 라이브러리를 concurrent.futures로 알고있습니다. 그 기반(low-level)은 threading과 multiprocessing으로 이해하고 있고요. 그런데 thread 강의에서는 대부분 concurrent.futures이용해서 multi-thread를 구현하셨는데, process 강의에서는 오히려 multiprocessing을 더 많이 쓰시는 것 같습니다. (concurrent.futures이용은 적어보임). 혹시 multi-processing은 concurrent.futures보다는 multiprocessing 라이브러리가 더 자주쓰이고 일반적인가요?
1. 숫자를 0부터1억까지 순서대로 더할때(0+1+2+...+1억), '1스레드/1프로세스'로 하는것과 '#스레드/#프로세스'로 하는것 중 어느것이 일반적으로 더 빠른가요? 잘은 모르지만... 숫자를 share하면서 가야하기때문에 (현재 어디까지 더한지를 알아야하니) '#스레드/#프로세스'는 굳이 switching(?)만 해야해서 더느릴것같은데... 이런생각이 맞나요? 더 나아가서 share하는 변수(상태를 연속적으로 알아야하는)가 있으면 하나의 스레드/프로세스에서 하는게 제일 빠르다고 생각해도 되나요?
2-1. parent thread/process에서 child thread/process를 만들고, 그 child가 할일을 다하면(더이상 실행할 코드가 없으면) 그것을 종료하는것이 자원관리에 효율적이라고 생각합니다. with문을 쓰면 자동으로 with문 내에서 정의된 child들이 다 끝나고 나서 with문을 나갈때 그 안에서 정의되었던 child들이 자원을 더이상 안쓴다고 생각하면 되나요? 그리고 역으로 with문은 with문 내에서 만들어진 child가 다 끝날때까지 with문 밖으로(들여쓰기 밖) 못나간다고 생각하는게 맞는 생각인가요?
2-2. 그러면 역으로 child는 무한으로 돌고, parent는 할일을 하고싶다면, with문내에서 그러한 child들을 정의하면 안될 것 같은데 (끝나지 않아 못나오니).... 이럴 경우에는 추후에 child가 끝나면, 제가 억지로 그 child의 자원사용을 막기위해 close(?)같은 걸 해야할 것 같은데... 어떻게하나요? 자동으로 파이썬이 작업을 안하는 therad/proecss는 자원을 안쓰도록 관리하나요? (ex. is_alive()나 .running()으로 찍었을때 False면 자원 안쓰도록)
3. (Thread부분이지만 Process에서도 동일할거라 생각) concurrent.futures.TheradPoolExecutor에서 submit을 통해 만들어지는 child thread가 daemon이 되도록 어떻게 설정하나요?
threading.Thread로 만들때는 argument에 daemon=True을 하면, 만들어지는 child thread는 daemon이 였는데, 위 경우는 잘 모르겠네요...
4. concurrent.futures.as_completed(Future Object)는 그 Future Object가 그것의 thread/process를 끝내면 바로 어떤작업을 처리하도록 해놓는것이고, thread/process가 끝난 시점에서 return값으로 Future Object 가 있다! 라고 이해하면 되나요?
그리고 그 return값을 갖고 무엇인가를 할때는 parent의 thread/process에서 하는게 맞는것이죠? (그 Future Object가 갖고있던 child thread/process가 아니라)
5. (4번이어서) 그러면 4번의 경우는 child thread/process에서 어떤작업들을 다하고, 그것의 결과를 가지고, parent thread/process에서 추가적인 작업을 할때 사용을 하면 되나요?
6. multi-process를 할때, 그 process의 개수는 제한이 없나요? (제 컴퓨터가 4Cores, 8Logical processors라 하더라도 process는 8개가 최대고 이런것은 없는것이죠??? 마치 task manager의 performance를 보면 Processes가 엄청많이 돌고있는것처럼, 제가 multi-process를 한다는것은 Processes를 그만큼 늘린다는것이고, 그것들을 어떻게 잘 할지는 8개의 Logical processors를 갖고 운영체제가 알아서 할일이라고 이해하면 되겠죠?)
※ multiprocessing을 하실때, 여러 process들을 다 만들어 start를 시키시고, parent process에서 .join()을 하시던데... 관련 2가지 질문이 있습니다. (아래 7번/8번)
7. process A/B/C를 만들어 start를 시키면 parent/A/B/C가 parallel하게 실행이 될텐데, 결국에 (강의에서는 for문을 이용) 순차적으로 parent에서 A.join() > B.join() > C.join()이 되도록 하면 parallel이 더이상 유지되지 않는것 아닌가요?
즉... A.join()을 만나는순간 모든 process는 멈추고 A process만 진행되다가 그것이 끝나면 다시 parent/B/C가 parallel하게 진행되고, 곧 B.join()을 만나는순간 모든 process는 멈추고 B process만 진행되다가 그것이 끝나면 다시 parent/C가 parallel하게 진행되고, 곧 C.join()을 만나면 모든 process는 멈추고 C process만 진행되다가 그것이 끝나면 다시 parent가 진행되고... 이 순서가 아닌가요? 그러면 multi-process의 parallel이, join을 만나는순간 깨지는 개념이 아닌가요?
8. (만일5의 제 의견이 맞다면) multi process에서 main process가 끝나더라도, 다른 child process는 계속해서 진행을 할텐데 (daemon이 아니라면), 굳이 join()을 써서 parallelism을 깨는 이유가 있나요?
thread에서는 잘 안쓰던 join인데, process에서는 join을 너무 당연하게 쓰셔서, 제가 놓치거나 모르는부분이 있나 싶어 여쭤봅니다.
9. multi-process의 변수 공유 관련하여 @ multiprocessing(4)-Sharing state 강의,
예시를 들어주신것이 parent process에서 share_value가 0이 나오고 '공유가 안되고있다' 라고 해주셨습니다. (강의 8분4초정도)
그리고 이어서 share_value를 다른 method에 (with 다른 process) 넘겼음에도 불구하고, share_value는 0이라고 해주셨습니다.
따라서 프로세스간 주소공간은 독립적이라고 해주셨는데... (프로세스간 object 공유가 안된다)
그런데 제 짧은 지식으로는, 이번경우는 mulit-process라서 공유가 안되었다기 보다는, 함수의 local변수와 그밖에서의 변수의 name space 때문이 아닌가요?
예를들면 아래코드 (single process) 에서도 share_value는 generate_update_number라는 함수를 다녀오더라도 0입니다.
그 이유는 share_value라는 object가 generate_update_number라는 함수안에서 한것은, return이 따로없다면, 그 밖에서는 영향이 없다! (제 짧은 지식으로는)
때문으로 이해가되는데... 혹시 아닐까요? (아래에 그대로 copy and paste 할 수 있도록 코드를 만들고, 실행결과도 붙였습니다.)
ㅡㅡㅡㅡㅡㅡㅡ 코드 ㅡㅡㅡㅡㅡㅡㅡ
def generate_update_number(v):
print(f"[step2] v의 값 / id : {v} / {id(v)}")
for _ in range(50):
v +=1
print(f"[step3] v의 값 / id : {v} / {id(v)}")
def main():
share_value = 0
print(f"[step1] share_value의 값 / id : {share_value} / {id(share_value)}")
generate_update_number(share_value)
print(f"[step4] share_value의 값 / id : {share_value} / {id(share_value)}")
if __name__=='__main__':
main()
ㅡㅡㅡㅡㅡㅡㅡ 실행결과 ㅡㅡㅡㅡㅡ
아니예요~ 후에 정리해서 한 번에 올려주세요!
답변드리겠습니다.