[íìŽì¬] Multiprocessing, Multithreading ìì ë¶í ë° í ë¹
ì볞 ê²ìêž: https://velog.io/@euisuk-chung/íìŽì¬-Multiprocessing-Multithreading-ìì-ë¶í -ë°-í ë¹
ë©í°íë¡ìžì±ê³Œ ë©í°ì€ë ë©ìì ìì ë¶í 곌 ìì
í ë¹ì ê°ê° ë€ë¥Žê² ìŽë£šìŽì§ëë€. ìŽ ê³Œì ì ìŽíŽíêž° ìíŽìë 뚌ì , 컎íší°ì ìì(í¹í CPUì ë©ëªšëЬ) ì¬ì© ë°©ì곌 ìì
ì ì¢
ë¥(ì: CPU ì§ìœì vs. I/O ì§ìœì )륌 ê³ ë €íŽìŒ í©ëë€.
ë©í°íë¡ìžì±(Multiprocessing)ì ìì ë¶í
ë©í°íë¡ìžì±ììë ê° íë¡ìžì€ê° ë 늜ë ë©ëªšëЬ ê³µê°ì ê°ì§ëë€. ìŽë ê° íë¡ìžì€ê° ìì€í ì ììì ë 늜ì ìŒë¡ í ë¹ë°ëë€ë ì믞ì ëë€. CPU ììì ë¶í ì ìŽì 첎ì ì ì€ìŒì€ë¬ì ìíŽ êŽëЬëë©°, ì¬ë¬ íë¡ìžì€ ê°ì CPU ìê°ì ê³µì íê² ë¶ë°°í©ëë€.
ìì ìŽ Nê°ì íë¡ìžì€ì ë¶ë°°ë ë, ìŽë¡ ì ìŒë¡ë ê° íë¡ìžì€ì ìì ì 1/Nì í ë¹í ì ììµëë€. íì§ë§ ì€ì ë¶í ì ìì ì ì±ê²©, íë¡ìžì€ì ì€í ìí, ìì€í ì ë€ë¥ž ì구 ì¬í ë±ì ë°ëŒ ë¬ëŒì§ ì ììµëë€. ì륌 ë€ìŽ, ìŽë€ íë¡ìžì€ë CPU륌 ë§ìŽ ì¬ì©íë ë°ë©Ž, ë€ë¥ž íë¡ìžì€ë ëêž° ìíì ìì ì ììµëë€.
ë©í°ì€ë ë©(Multithreading)ì ìì ë¶í
ë©í°ì€ë ë©ììë 몚ë ì€ë ëê° íë¡ìžì€ì ë©ëªšëЬ ê³µê°ì ê³µì í©ëë€. ë°ëŒì, ë©ëªšëЬ ììì ë³ëë¡ ë¶í ëì§ ìê³ , 몚ë ì€ë ëê° ê°ì ë©ëªšëЬ ììì ì ê·Œí ì ììµëë€. CPU ììì 겜ì°, ìŽì 첎ì ì ì€ë ë ì€ìŒì€ë¬ê° ê° ì€ë ëì CPU ìê°ì í ë¹í©ëë€.
ì€ë ë ê°ì ìì ë¶í ì íë¡ìžì€ ëŽìì ìŽë£šìŽì§ë©°, ì€ë ëê° ìííë ìì ì ìŒë°ì ìŒë¡ ë ìê³ , 구첎ì ìž ìì ëšìë¡ ëëìŽì§ëë€. ë©í°ì€ë ë© í겜ììë I/O ìì ìŽ ì§íëë ëì ë€ë¥ž ì€ë ëê° CPU륌 ì¬ì©í ì ììŽ, ì 첎ì ìž íë¡ê·žëšì íšìšì±ì ëìŒ ì ììµëë€.
â (Q&A) ì¬êž°ì ì ê¹!!
Q. ë©í°íë¡ìžì€ìì
íë¡ìžì€Aìíë¡ìžì€Bê° ìë€ê³ ê°ì íŽë³Žê² ìµëë€. ê·žëŒ 4ìŽëŒë ê°ì© ìììŽ ììë ë§ìœ íë¡ìžì€Aê° ëšŒì ëë멎 ëëšžì§ íë¡ìžì€ìž íë¡ìžì€Bê° ëšì ìì 4ê°ë¥Œ ë€ ì°ëë¡ ë°ëëë¡ ìëìŒë¡ í ë¹íŽì£Œëì?A. ì€ì ë¡ íë¡ìžì€Aê° ì¢ ë£ëìŽ ìììŽ ë°íë ë, íë¡ìžì€Bê° ìëìŒë¡ 몚ë ììì íì©íëë¡ ìì€í ìŽ ì¬ì¡°ì ëë ê²ì 볎ì¥ëì§ ììµëë€. ììì ì¬í ë¹ì ìŽì 첎ì ì ì€ìŒì€ë§ ìê³ ëŠ¬ìŠê³Œ ì ì± ì ë°ëŒ ê²°ì ëë©°, ë€ë¥ž ëêž° ì€ìž íë¡ìžì€ë ìì€í ì ì ë°ì ìž ìì êŽëЬ ì ëµì ìíŽ ìí¥ì ë°ìµëë€. íë¡ìžì€Bê° ì¶ê° ììì íì©íë €ë©Ž, íŽë¹ íë¡ìžì€ê° ë³ë ¬ ì²ëŠ¬ë¥Œ ì§ìíê³ ì¶ê° CPU ìœìŽë¥Œ íšìšì ìŒë¡ íì©í ì ìë êµ¬ì¡°ë¡ ì€ê³ëìŽìŒ í©ëë€.
â ì¶ê° ì€ëª : ìì í ë¹ì ëì ë°©ì
â CPU ìœìŽ í ë¹: ê°ì© ìììŽ CPU ìœìŽëŒê³ í ë, ê° íë¡ìžì€ë ìŽì 첎ì ì ìíŽ íë ìŽìì CPU ìœìŽì í ë¹ë ì ììµëë€. íë¡ìžì€Aì íë¡ìžì€Bê° ê°ê° 2ê°ì CPU ìœìŽë¥Œ ì¬ì©íê³ ìë€ë©Ž, ìŽë íë¡ìžì€ê° ì€íëë ëì ëìì 2ê°ì ìì ì ìíí ì ììì ì믞í©ëë€.
â ëì ì¬í ë¹: íë¡ìžì€Aê° ìì ì ìë£íê³ ì¢ ë£ë멎, ê·ž íë¡ìžì€ì í ë¹ëìë ìì(ì¬êž°ìë CPU ìœìŽ)ì ìì€í ì ë°íë©ëë€. ìŽí ìŽì 첎ì ì ì€ìŒì€ë¬ë ìŽì ì¬ì© ê°ë¥í ììì ë€ì ë¶ë°°í ì ìê² ë©ëë€. íì§ë§, ìëìŒë¡ íë¡ìžì€Bê° ëëšžì§ ììì 몚ë ì¬ì©íëë¡ í ë¹ë°ë ê²ì ìëìŒë¡ ìŽë£šìŽì§ì§ ììµëë€. â ììì ì¬í ë¹ ì¡°ê±Ž: íë¡ìžì€Bê° ì¶ê°ì ìž CPU ìœìŽë¥Œ ì¬ì©í ì ìê² ëëì§ ì¬ë¶ë ì¬ë¬ ììì ìíŽ ê²°ì ë©ëë€. ìŽë ìŽì 첎ì ì ì€ìŒì€ë§ ì ì± , íë¡ìžì€ì ì°ì ìì, ê·žëŠ¬ê³ íë¡ìžì€Bê° ì¶ê° ììì íšê³Œì ìŒë¡ íì©í ì ìëì§(ì: ë³ë ¬ ì²ëŠ¬ê° ê°ë¥í ìì ìžì§)ì ë°ëŒ ë¬ëŒì§ ì ììµëë€. â ìì ì¬ì©ì ìµì í: ëë¶ë¶ì íë ìŽì 첎ì ë ìì€í ììì íšìšì ìŒë¡ íì©íêž° ìí ë³µì¡í ì€ìŒì€ë§ ìê³ ëŠ¬ìŠì ì¬ì©í©ëë€. íë¡ìžì€Aê° ì¢ ë£ëìŽ ìììŽ ë°íë멎, ìŽ ììì ìì€í ì ë€ë¥ž ëêž° ì€ìž íë¡ìžì€ì í ë¹ë ì ììµëë€. ê·žë¬ë, í¹ì íë¡ìžì€ê° ìëìŒë¡ 몚ë ê°ì© ììì ë ì íëë¡ ìì€í ìŽ ì¬ì¡°ì íë ê²ì ìŒë°ì ìž ê²œì°ê° ìëëë€.
ìì í ë¹ ë°©ì
ì€ì ìì í ë¹ì ì í늬ìŒìŽì ë 벚ìì ê°ë°ìê° ê²°ì í©ëë€. ë©í°íë¡ìžì±ìŽë ë©í°ì€ë ë©ì ì¬ì©í ë, ê°ê°ì íë¡ìžì€ë ì€ë ëì í ë¹í ìì ì í¬êž°ì ë²ìë íë¡ê·žëšì 구조ì ì구 ì¬íì ë°ëŒ ë¬ëŒì§ëë€. ì륌 ë€ìŽ, ë°ìŽí° ì²ëЬ ìì ì ì¬ë¬ íë¡ìžì€ì ë¶ë°°í ëë ê° íë¡ìžì€ê° ì²ëЬí ë°ìŽí°ì ìì ê· ë±íê² ëëê±°ë, í¹ì 조걎ì ë§ë ë°ìŽí°ë¥Œ í ë¹íë ë°©ììŒë¡ ë¶í í ì ììµëë€.
ë©í°íë¡ìžì±ê³Œ ë©í°ì€ë ë© ëªšë ìì
ì ë³ë ¬ ì²ëŠ¬ë¥Œ íµíŽ ì±ë¥ì í¥ììí€ì§ë§, ê·ž 구í ë°©ì곌 ì¬ì© ì¬ë¡ë í¬ê² ë€ëŠ
ëë€. ë©í°íë¡ìžì±ì ë
늜ì ìž ìì
ìŽ ë§ê³ , ê° ìì
ìŽ ìë¹í ìì CPU ììì íìë¡ í ë ì 늬íë©°, ë©í°ì€ë ë©ì ìì
ê°ì ììì ê³µì íŽìŒ íê±°ë I/O ë°ìŽë ìì
ìŽ ë§ì ë íšê³Œì ì
ëë€.
ìì í ë¹ê³Œ ìì ë¶ë°° ë°©ìì ëíŽ ë ììží ì€ëª íê² ìµëë€. Ʞ볞ì ìŒë¡, ë©í°íë¡ìžì±ê³Œ ë©í°ì€ë ë© í겜ììì ìì í ë¹ì ìë곌 ìë(ê°ë°ì ì§ì ) ë°©ìì 몚ë í¬íší ì ììµëë€.
ìë í ë¹(ìì€í )
- ê°ì© ììì ìë ë¶ë°°: ìŽì 첎ì ì ì€ìŒì€ë¬ê° íë¡ìžì€ë ì€ë ëì ëíŽ CPU ìê°ì ìëìŒë¡ í ë¹í©ëë€. ìŽ ê²œì°, ì€ìŒì€ë¬ë ìì€í ì íì¬ ë¶í, íë¡ìžì€ì ì°ì ìì, íë¡ìžì€ì ìí(ì€í ì€, ëêž° ì€ ë±)ì ê°ì ì¬ë¬ ììžì ê³ ë €íì¬ ììì ë¶ë°°í©ëë€. ëíŽížë¡, ìì€í ì ê°ë¥í ê³µì íê² ììì ë¶ë°°íë €ê³ ìëíì§ë§, ìŽë â1/NâìŽëŒë ê³ ì ë¹ìšë¡ ì íí ë¶ë°°ëë€ë ì믞ë ìëëë€.
ìë í ë¹(ê°ë°ì ì§ì )
- ê°ë°ìì ìí ëª ìì ë¶ë°°: ê°ë°ìë íë¡ê·žëšì ì구 ì¬í곌 ìì ì í¹ì±ì ê³ ë €íì¬, ê° íë¡ìžì€ë ì€ë ëì í ë¹ë ìì ì ììŽë ììì ì¬ì©ì ëª ìì ìŒë¡ ì§ì í ì ììµëë€. ì륌 ë€ìŽ, ë°ìŽí° ì²ëЬ ìì ì ì¬ë¬ íë¡ìžì€ì ë¶ë°°í ë í¹ì êž°ì€ì ë°ëŒ ìì ì ëë ì ììŒë©°, ìŽë ìì ì íšìšì±ê³Œ ì€í ìê°ì ìí¥ì ë¯žì¹ ì ììµëë€.
ë©í°íë¡ìžì± ìì í ë¹ ìì
ìŽ ììììë ëëì ë°ìŽí°ë¥Œ 4ê°ì ì²í¬ë¡ ëëê³ , ê° ì²í¬ë¥Œ ë³ëì íë¡ìžì€ì í ë¹íì¬ ë³ë ¬ë¡ ì²ëЬí©ëë€. ìŽë ê°ë°ìê° ìì ì ë¶ë°°ë¥Œ ëª ìì ìŒë¡ ì§ì í 겜ì°ì ëë€.
1
2
3
4
5
6
7
8
9
10
11
12
13
from multiprocessing import Pool
def my_task(data_chunk):
# ë³µì¡í ê³ì° ìí
result = sum(data_chunk)
return result
if __name__ == "__main__":
data = range(1000000) # ëëì ë°ìŽí°
chunks = [data[i::4] for i in range(4)] # ë°ìŽí°ë¥Œ 4ê°ì ì²í¬ë¡ ëë
with Pool(4) as p: # 4ê°ì íë¡ìžì€ í ìì±
results = p.map(my_task, chunks) # ê° ì²í¬ë¥Œ ë³ëì íë¡ìžì€ì í ë¹
ì íìŽì¬ ìœëìì multiprocessing.Poolì ì¬ì©íì¬ ë©í°íë¡ìžì±ì 구íí ë, ìì í ë¹ì ë€ì곌 ê°ìŽ ìŽë£šìŽì§ëë€:
íë¡ìžì€ í곌 CPU ìœìŽ í ë¹
íë¡ìžì€ í ìì±: Pool(4)ì ìíŽ 4ê°ì ë³ë íë¡ìžì€ê° ìì±ë©ëë€. ìŽë ë©í°íë¡ìžì±ì ìí íë¡ìžì€ íë¡, ë³ë ¬ ìì ì ìííêž° ìíŽ ì€ë¹ë íë¡ìžì€ ì§í©ì ëë€.CPU ìœìŽ ì¬ì©: ìì€í ì 8ê°ì CPU ìœìŽê° ìë 겜ì°, Pool(4)ì ìíŽ ìì±ë ê° íë¡ìžì€ë ìŽì© ê°ë¥í CPU ìœìŽ ì€ íë륌 ì¬ì©í ì ìê² ë©ëë€. ì¬êž°ì ì€ìí ì ì, íë¡ìžì€ íì ìíŽ ìì±ë íë¡ìžì€ ìê° ìì€í ì CPU ìœìŽ ìë³Žë€ ì êž° ë묞ì, 몚ë íë¡ìžì€ê° ëìì ì€íë ì ììŒë©°, ê°ê° ë³ëì CPU ìœìŽì í ë¹ë ê°ë¥ì±ìŽ ëìµëë€.
ìì ì²ëЬ ë°©ì
ë³ë ¬ ì²ëЬ: ìì±ë 4ê°ì íë¡ìžì€ë ëìì ì€íëìŽ ê°ê° í ë¹ë ìì (ë°ìŽí° ì²í¬)ì ì²ëЬí©ëë€. ìì€í ì ì¬ì ìœìŽê° ì¶©ë¶í ìêž° ë묞ì, ìŽ íë¡ìžì€ë€ì ìë¡ ê²œì ììŽ ê°ìì ìœìŽìì ì€íë ì ììµëë€.ìì íì© ìµì í: 8ê°ì ìœìŽ ì€ 4ê°ë§ ì¬ì©ëë¯ë¡, ëëšžì§ ìœìŽë ìì€í ì ë€ë¥ž íë¡ìžì€ë ìì ì ì¬ì©ë ì ììµëë€. ìŽë ë©í°íì€í¹ í겜ìì ìì€í ììì íšìšì ìŒë¡ íì©í ì ìê² íŽì€ëë€.
(ì°žê³ ) ê·žëŒ ìŽë»ê² íŽìŒ ê°ì© ììì ë€ ìž ì ììê¹?
íìŽì¬ì
multiprocessing몚ëì íë¡ìžì€ë¹ CPU ìœìŽ ì륌 ì§ì ì§ì íë êž°ë¥ì ì§ì ì ê³µíì§ ììµëë€. CPU ìœìŽì í ë¹ê³Œ ì€ìŒì€ë§ì ìŽì 첎ì ì ìì ìŽë©°,multiprocessingëŒìŽëžë¬ëЬë ìŽë¬í ë®ì ìì€ì ìì êŽëЬì ì§ì ê°ì íì§ ììµëë€. ê·žë¬ë, ì 첎 CPU ìœìŽë¥Œ ìµëí íì©íë €ë 목ì ìŽëŒë©Ž, ìì ì ë ë§ì íë¡ìžì€ì ë¶ë°°íê±°ë ìì€í ì ë³ë ¬ ì²ëЬ ë¥ë ¥ì ìµëë¡ íì©íë ë°©ë²ì ê³ ë €í ì ììµëë€. ã € ì 첎 CPU ìœìŽ ì¬ì©íêž° : ìì€í ì ìë 몚ë CPU ìœìŽë¥Œ ì¬ì©íë €ë©Ž,multiprocessing.cpu\_count()íšì륌 ì¬ì©íì¬ ìì€í ì ìë ìœìŽì ì륌 íìžíê³ , ìŽë¥ŒPoolì ìžìë¡ ì¬ì©í ì ììµëë€. ìŽ ë°©ë²ì ìì€í ì 몚ë CPU ìœìŽë¥Œ íì©íì¬ ë³ë ¬ ì²ëŠ¬ë¥Œ ìííë €ë ê²œì° ì ì©í©ëë€. ìë ìì륌 ì°žê³ íŽì£Œìžì ð€
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
# ì 첎 CPU ìœìŽ ì¬ì©íêž°
from multiprocessing import Pool, cpu_count
def my_task(data_chunk):
# ë³µì¡í ê³ì° ìí
result = sum(data_chunk)
return result
if __name__ == "__main__":
data = range(1000000) # ëëì ë°ìŽí°
num_cores = cpu_count() # ìì€í
ì CPU ìœìŽ ì íìž
chunks = [data[i::num_cores] for i in range(num_cores)] # ë°ìŽí°ë¥Œ CPU ìœìŽ ìë§íŒ ì²í¬ë¡ ëë
with Pool(num_cores) as p: # ìì€í
ì 몚ë CPU ìœìŽ ì¬ì©
results = p.map(my_task, chunks)
ë©í°ì€ë ë© ìì í ë¹ ìì
ìŽ ë©í°ì€ë ë© ììììë ì 첎 ìì ë²ì륌 4ê°ë¡ ëëìŽ ê° ì€ë ëê° ì²ëЬíëë¡ í©ëë€. ìŽë¬í ë°©ìì ê° ì€ë ëê° ì²ëЬí ë°ìŽí°ì ìì ëª ííê² ì§ì í ì ìêž° ë묞ì, ìì ì ë¶ë°°ê° ê³ ë¥Žê² ìŽë£šìŽì§ëë¡ í ì ììµëë€.
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
import threading
def my_task(start, end):
# ë²ì ëŽì ì«ì í©ê³ ê³ì°
result = sum(range(start, end))
print(f"Result: {result}")
threads = []
for i in range(4):
# ì 첎 ë²ì륌 4ê°ì ë¶ë¶ìŒë¡ ëëìŽ ê° ì€ë ëì í ë¹
t = threading.Thread(target=my_task, args=(250000*i, 250000*(i+1)))
threads.append(t)
t.start()
for t in threads:
t.join()
ì ìë ìì ìœëììë ê° ì€ë ëê° ì²ëЬíë ë°ìŽí°ì ìì ê· ë±íê² ë¶ë°°íììµëë€. ìŽë ê° ìì ëšìê° ëìŒí ììì ìë¹íë€ê³ ê°ì íì ë ì ì©í©ëë€. íì§ë§ ì€ì ë¡ë ìì ì ë³µì¡ëê° ë€ë¥Œ ì ììŒë¯ë¡, ììì ìžêží ë°©ë²ë€ì íµíŽ ìì ë¶ë°° ë°©ìì ë³Žë€ ìžë°íê² ì¡°ì í íìê° ììµëë€.
ìì ì ë³µì¡ëë ìì ìë¹ëì ê³ ë €íì¬ ìì ì í ë¹íë ê²ì ë©í°ì€ë ë© íë¡ê·žëšì ì±ë¥ì ìµì ííë ë° ì€ìí ììì ëë€. ë°ëŒì, ìì ì í¹ì±ì ì íí íì íê³ , ìŽì êž°ë°í ì ì í ìì ë¶ë°° ì ëµì ì ííë ê²ìŽ ì€ìí©ëë€.
(ì°žê³ ) ê·žë ë€ë©Ž ìŽë»ê² ë©í°ì€ë ë© íë¡ê·žëšì ì±ë¥ì ìµì í륌 í ê¹?
queue륌 ì¬ì©íŽì taskì ë³µì¡ë륌 ì ìíê³ ìŽë¥Œ argsë¡ ë£ìŽì£Œë©Ž ê° ì€ë ëê° ì²ëЬí ë°ìŽí°ì ìì ëª ííê² ì ìíŽì€ ì ììµëë€. (ìë ìììœë ì°žê³ )
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
import time
import queue
# ìì
íšì
def worker(work_queue):
while not work_queue.empty():
try:
# íìì ìì
ì ê°ì žìŽ
task = work_queue.get_nowait()
except queue.Empty:
break
# ìì
ì ëŽì©(ì¬êž°ìë ëšìí ìŒì ìê° ëêž°íë ê²ìŒë¡ ê°ì )
print(f"{threading.current_thread().name} is processing task: {task}")
time.sleep(task)
print(f"{threading.current_thread().name} finished task: {task}")
# ìì
ìë£ë¥Œ íì ì늌
work_queue.task_done()
# ìì
í ìì± ë° ìì
ì¶ê°
work_queue = queue.Queue()
tasks = [2, 4, 6, 8, 1, 3, 5, 7] # ê° ì«ìë ìì
ì "ë³µì¡ë"륌 ëíë(ì: ì²ëЬ ìê°)
for task in tasks:
work_queue.put(task)
# ì€ë ë ìì± ë° ìì
num_threads = 4
threads = []
for i in range(num_threads):
t = threading.Thread(target=worker, args=(work_queue,))
t.start()
threads.append(t)
# 몚ë ì€ë ëì ìì
ìŽ ìë£ë ëê¹ì§ ëêž°
for t in threads:
t.join()
print("All tasks are completed.")