[{"data":1,"prerenderedAt":1252},["ShallowReactive",2],{"blog-\u002Fblog\u002Fpython-threading-vs-multiprocessing-vs-asyncio":3},{"id":4,"title":5,"body":6,"description":1239,"difficulty":1240,"extension":1241,"framework":1242,"frameworkSlug":76,"meta":1243,"navigation":98,"order":128,"path":1244,"qaPath":565,"seo":1245,"stem":1246,"subtopic":1247,"topic":1248,"topicSlug":1249,"updated":1250,"__hash__":1251},"blog\u002Fblog\u002Fpython-threading-vs-multiprocessing-vs-asyncio.md","Python threading vs multiprocessing vs asyncio — Which to Use When",{"type":7,"value":8,"toc":1224},"minimark",[9,14,39,43,54,71,225,231,235,381,387,394,539,550,556,567,573,583,704,715,721,786,798,806,812,836,995,1028,1040,1048,1052,1060,1066,1082,1090,1095,1184,1195,1203,1207,1220],[10,11,13],"h2",{"id":12},"one-problem-three-different-solutions","One problem, three different solutions",[15,16,17,18,22,23,26,27,30,31,34,35,38],"p",{},"Python has three concurrency models — ",[19,20,21],"strong",{},"threading",", ",[19,24,25],{},"multiprocessing",", and ",[19,28,29],{},"asyncio","\n— and which one you pick determines whether your program gets faster or just gets more\ncomplicated. The choice is not arbitrary: each model solves a different bottleneck. Get\nit wrong and you'll add overhead without gaining any throughput. The key to choosing\ncorrectly is understanding two concepts: the ",[19,32,33],{},"GIL"," and the ",[19,36,37],{},"I\u002FO-bound vs CPU-bound\ndistinction",".",[10,40,42],{"id":41},"the-gil-why-threading-isnt-always-parallel","The GIL — why threading isn't always parallel",[15,44,45,46,49,50,53],{},"CPython's ",[19,47,48],{},"Global Interpreter Lock (GIL)"," is a mutex that allows only one thread to\nexecute Python bytecode at a time. On a four-core machine, a multithreaded Python program\nstill runs its bytecode on ",[19,51,52],{},"one core at a time",". Threads take turns; they don't run\nsimultaneously for CPU work.",[15,55,56,57,60,61,65,66,70],{},"The GIL is released around ",[19,58,59],{},"blocking I\u002FO"," (network calls, file reads, ",[62,63,64],"code",{},"sleep",") — so\nmultiple threads ",[67,68,69],"em",{},"can"," overlap those waits. But for pure Python number-crunching, threads\ncan't use more than one core.",[72,73,78],"pre",{"className":74,"code":75,"language":76,"meta":77,"style":77},"language-python shiki shiki-themes github-light github-dark","import threading\n\ndef burn_cpu():\n    total = 0\n    for _ in range(10_000_000):\n        total += 1   # holds the GIL the whole time\n\nt1 = threading.Thread(target=burn_cpu)\nt2 = threading.Thread(target=burn_cpu)\nt1.start(); t2.start()\nt1.join();  t2.join()\n# Takes ~same time as running sequentially — GIL serialises bytecode\n","python","",[62,79,80,93,100,113,126,150,166,171,191,207,213,219],{"__ignoreMap":77},[81,82,85,89],"span",{"class":83,"line":84},"line",1,[81,86,88],{"class":87},"szBVR","import",[81,90,92],{"class":91},"sVt8B"," threading\n",[81,94,96],{"class":83,"line":95},2,[81,97,99],{"emptyLinePlaceholder":98},true,"\n",[81,101,103,106,110],{"class":83,"line":102},3,[81,104,105],{"class":87},"def",[81,107,109],{"class":108},"sScJk"," burn_cpu",[81,111,112],{"class":91},"():\n",[81,114,116,119,122],{"class":83,"line":115},4,[81,117,118],{"class":91},"    total ",[81,120,121],{"class":87},"=",[81,123,125],{"class":124},"sj4cs"," 0\n",[81,127,129,132,135,138,141,144,147],{"class":83,"line":128},5,[81,130,131],{"class":87},"    for",[81,133,134],{"class":91}," _ ",[81,136,137],{"class":87},"in",[81,139,140],{"class":124}," range",[81,142,143],{"class":91},"(",[81,145,146],{"class":124},"10_000_000",[81,148,149],{"class":91},"):\n",[81,151,153,156,159,162],{"class":83,"line":152},6,[81,154,155],{"class":91},"        total ",[81,157,158],{"class":87},"+=",[81,160,161],{"class":124}," 1",[81,163,165],{"class":164},"sJ8bj","   # holds the GIL the whole time\n",[81,167,169],{"class":83,"line":168},7,[81,170,99],{"emptyLinePlaceholder":98},[81,172,174,177,179,182,186,188],{"class":83,"line":173},8,[81,175,176],{"class":91},"t1 ",[81,178,121],{"class":87},[81,180,181],{"class":91}," threading.Thread(",[81,183,185],{"class":184},"s4XuR","target",[81,187,121],{"class":87},[81,189,190],{"class":91},"burn_cpu)\n",[81,192,194,197,199,201,203,205],{"class":83,"line":193},9,[81,195,196],{"class":91},"t2 ",[81,198,121],{"class":87},[81,200,181],{"class":91},[81,202,185],{"class":184},[81,204,121],{"class":87},[81,206,190],{"class":91},[81,208,210],{"class":83,"line":209},10,[81,211,212],{"class":91},"t1.start(); t2.start()\n",[81,214,216],{"class":83,"line":215},11,[81,217,218],{"class":91},"t1.join();  t2.join()\n",[81,220,222],{"class":83,"line":221},12,[81,223,224],{"class":164},"# Takes ~same time as running sequentially — GIL serialises bytecode\n",[15,226,227,228],{},"This is why the first question to answer before picking a model is: ",[19,229,230],{},"what is the\nbottleneck?",[10,232,234],{"id":233},"quick-reference-comparison","Quick-reference comparison",[236,237,238,259],"table",{},[239,240,241],"thead",{},[242,243,244,247,251,255],"tr",{},[245,246],"th",{},[245,248,249],{},[62,250,21],{},[245,252,253],{},[62,254,25],{},[245,256,257],{},[62,258,29],{},[260,261,262,279,295,311,333,349,365],"tbody",{},[242,263,264,270,273,276],{},[265,266,267],"td",{},[19,268,269],{},"Parallelism",[265,271,272],{},"No (GIL)",[265,274,275],{},"Yes — one process per CPU core",[265,277,278],{},"No — cooperative, single-threaded",[242,280,281,286,289,292],{},[265,282,283],{},[19,284,285],{},"Best for",[265,287,288],{},"I\u002FO-bound (many slow calls)",[265,290,291],{},"CPU-bound (number crunching, image processing)",[265,293,294],{},"I\u002FO-bound (many concurrent connections)",[242,296,297,302,305,308],{},[265,298,299],{},[19,300,301],{},"Memory model",[265,303,304],{},"Shared",[265,306,307],{},"Separate per process",[265,309,310],{},"Shared (single thread)",[242,312,313,318,321,324],{},[265,314,315],{},[19,316,317],{},"Communication",[265,319,320],{},"Easy (shared objects, locks)",[265,322,323],{},"Explicit (Queue, Pipe, Value) — must pickle",[265,325,326,329,330],{},[62,327,328],{},"await",", queues, ",[62,331,332],{},"asyncio.gather",[242,334,335,340,343,346],{},[265,336,337],{},[19,338,339],{},"Overhead",[265,341,342],{},"Light (OS threads)",[265,344,345],{},"Heavy (process spawn + pickle)",[265,347,348],{},"Minimal (coroutines are cheap)",[242,350,351,356,359,362],{},[265,352,353],{},[19,354,355],{},"Complexity",[265,357,358],{},"Medium (race conditions)",[265,360,361],{},"Medium (serialisation)",[265,363,364],{},"Medium (async\u002Fawait syntax)",[242,366,367,372,375,378],{},[265,368,369],{},[19,370,371],{},"Max concurrency",[265,373,374],{},"~100–1 000 threads (OS limit)",[265,376,377],{},"~CPU core count",[265,379,380],{},"Tens of thousands of coroutines",[10,382,384,386],{"id":383},"threading-overlapping-io-waits",[62,385,21],{}," — overlapping I\u002FO waits",[15,388,389,390,393],{},"Use threading when your program makes ",[19,391,392],{},"many blocking I\u002FO calls"," and you want them to\noverlap. While one thread is blocked waiting on a network response, another thread can\nrun. The GIL doesn't block during I\u002FO waits, so you get real concurrency for free.",[72,395,397],{"className":74,"code":396,"language":76,"meta":77,"style":77},"import threading, urllib.request\n\ndef fetch(url):\n    with urllib.request.urlopen(url) as r:\n        return len(r.read())     # GIL released while waiting for network\n\nurls = [\"https:\u002F\u002Fexample.com\"] * 10\nthreads = [threading.Thread(target=fetch, args=(u,)) for u in urls]\nfor t in threads: t.start()\nfor t in threads: t.join()\n# All 10 requests overlap — ~10x faster than sequential\n",[62,398,399,406,410,420,434,448,452,475,511,523,534],{"__ignoreMap":77},[81,400,401,403],{"class":83,"line":84},[81,402,88],{"class":87},[81,404,405],{"class":91}," threading, urllib.request\n",[81,407,408],{"class":83,"line":95},[81,409,99],{"emptyLinePlaceholder":98},[81,411,412,414,417],{"class":83,"line":102},[81,413,105],{"class":87},[81,415,416],{"class":108}," fetch",[81,418,419],{"class":91},"(url):\n",[81,421,422,425,428,431],{"class":83,"line":115},[81,423,424],{"class":87},"    with",[81,426,427],{"class":91}," urllib.request.urlopen(url) ",[81,429,430],{"class":87},"as",[81,432,433],{"class":91}," r:\n",[81,435,436,439,442,445],{"class":83,"line":128},[81,437,438],{"class":87},"        return",[81,440,441],{"class":124}," len",[81,443,444],{"class":91},"(r.read())     ",[81,446,447],{"class":164},"# GIL released while waiting for network\n",[81,449,450],{"class":83,"line":152},[81,451,99],{"emptyLinePlaceholder":98},[81,453,454,457,459,462,466,469,472],{"class":83,"line":168},[81,455,456],{"class":91},"urls ",[81,458,121],{"class":87},[81,460,461],{"class":91}," [",[81,463,465],{"class":464},"sZZnC","\"https:\u002F\u002Fexample.com\"",[81,467,468],{"class":91},"] ",[81,470,471],{"class":87},"*",[81,473,474],{"class":124}," 10\n",[81,476,477,480,482,485,487,489,492,495,497,500,503,506,508],{"class":83,"line":173},[81,478,479],{"class":91},"threads ",[81,481,121],{"class":87},[81,483,484],{"class":91}," [threading.Thread(",[81,486,185],{"class":184},[81,488,121],{"class":87},[81,490,491],{"class":91},"fetch, ",[81,493,494],{"class":184},"args",[81,496,121],{"class":87},[81,498,499],{"class":91},"(u,)) ",[81,501,502],{"class":87},"for",[81,504,505],{"class":91}," u ",[81,507,137],{"class":87},[81,509,510],{"class":91}," urls]\n",[81,512,513,515,518,520],{"class":83,"line":193},[81,514,502],{"class":87},[81,516,517],{"class":91}," t ",[81,519,137],{"class":87},[81,521,522],{"class":91}," threads: t.start()\n",[81,524,525,527,529,531],{"class":83,"line":209},[81,526,502],{"class":87},[81,528,517],{"class":91},[81,530,137],{"class":87},[81,532,533],{"class":91}," threads: t.join()\n",[81,535,536],{"class":83,"line":215},[81,537,538],{"class":164},"# All 10 requests overlap — ~10x faster than sequential\n",[15,540,541,542,545,546,549],{},"The catch: shared mutable state across threads creates ",[19,543,544],{},"race conditions",". You need\n",[62,547,548],{},"threading.Lock"," around any object multiple threads write to.",[15,551,552,555],{},[19,553,554],{},"Rule of thumb:"," threading works well for a modest number (dozens to low hundreds) of\nconcurrent blocking calls where you want simple, familiar code.",[557,558,559],"blockquote",{},[15,560,561,562],{},"Deep dive: ",[563,564,566],"a",{"href":565},"\u002Fpython\u002Fconcurrency\u002Fgil","Threading & the GIL interview questions",[10,568,570,572],{"id":569},"multiprocessing-true-cpu-parallelism",[62,571,25],{}," — true CPU parallelism",[15,574,575,576,579,580,582],{},"When work is ",[19,577,578],{},"CPU-bound"," — image processing, data transformation, machine learning\npreprocessing, cryptography — you need multiple CPU cores running in true parallel.\n",[62,581,25],{}," spawns separate Python processes, each with its own GIL and memory\nspace, so they can use all available cores simultaneously.",[72,584,586],{"className":74,"code":585,"language":76,"meta":77,"style":77},"from multiprocessing import Pool\n\ndef cpu_work(n):\n    return sum(i * i for i in range(n))   # pure CPU — needs real parallelism\n\nwith Pool(processes=4) as pool:           # 4 worker processes\n    results = pool.map(cpu_work, [1_000_000] * 8)\n# Runs on 4 cores in parallel — ~4x faster on a quad-core machine\n",[62,587,588,601,605,615,645,649,676,699],{"__ignoreMap":77},[81,589,590,593,596,598],{"class":83,"line":84},[81,591,592],{"class":87},"from",[81,594,595],{"class":91}," multiprocessing ",[81,597,88],{"class":87},[81,599,600],{"class":91}," Pool\n",[81,602,603],{"class":83,"line":95},[81,604,99],{"emptyLinePlaceholder":98},[81,606,607,609,612],{"class":83,"line":102},[81,608,105],{"class":87},[81,610,611],{"class":108}," cpu_work",[81,613,614],{"class":91},"(n):\n",[81,616,617,620,623,626,628,631,633,635,637,639,642],{"class":83,"line":115},[81,618,619],{"class":87},"    return",[81,621,622],{"class":124}," sum",[81,624,625],{"class":91},"(i ",[81,627,471],{"class":87},[81,629,630],{"class":91}," i ",[81,632,502],{"class":87},[81,634,630],{"class":91},[81,636,137],{"class":87},[81,638,140],{"class":124},[81,640,641],{"class":91},"(n))   ",[81,643,644],{"class":164},"# pure CPU — needs real parallelism\n",[81,646,647],{"class":83,"line":128},[81,648,99],{"emptyLinePlaceholder":98},[81,650,651,654,657,660,662,665,668,670,673],{"class":83,"line":152},[81,652,653],{"class":87},"with",[81,655,656],{"class":91}," Pool(",[81,658,659],{"class":184},"processes",[81,661,121],{"class":87},[81,663,664],{"class":124},"4",[81,666,667],{"class":91},") ",[81,669,430],{"class":87},[81,671,672],{"class":91}," pool:           ",[81,674,675],{"class":164},"# 4 worker processes\n",[81,677,678,681,683,686,689,691,693,696],{"class":83,"line":168},[81,679,680],{"class":91},"    results ",[81,682,121],{"class":87},[81,684,685],{"class":91}," pool.map(cpu_work, [",[81,687,688],{"class":124},"1_000_000",[81,690,468],{"class":91},[81,692,471],{"class":87},[81,694,695],{"class":124}," 8",[81,697,698],{"class":91},")\n",[81,700,701],{"class":83,"line":173},[81,702,703],{"class":164},"# Runs on 4 cores in parallel — ~4x faster on a quad-core machine\n",[15,705,706,707,710,711,714],{},"The cost is ",[19,708,709],{},"process startup overhead"," and the requirement that all data crossing\nprocess boundaries must be ",[19,712,713],{},"picklable",". Lambda functions, file handles, and database\nconnections can't be pickled — they must stay inside the worker.",[15,716,717,720],{},[62,718,719],{},"concurrent.futures.ProcessPoolExecutor"," provides the same power with a cleaner API:",[72,722,724],{"className":74,"code":723,"language":76,"meta":77,"style":77},"from concurrent.futures import ProcessPoolExecutor\n\nwith ProcessPoolExecutor(max_workers=4) as ex:\n    results = list(ex.map(cpu_work, [1_000_000] * 8))\n",[62,725,726,738,742,763],{"__ignoreMap":77},[81,727,728,730,733,735],{"class":83,"line":84},[81,729,592],{"class":87},[81,731,732],{"class":91}," concurrent.futures ",[81,734,88],{"class":87},[81,736,737],{"class":91}," ProcessPoolExecutor\n",[81,739,740],{"class":83,"line":95},[81,741,99],{"emptyLinePlaceholder":98},[81,743,744,746,749,752,754,756,758,760],{"class":83,"line":102},[81,745,653],{"class":87},[81,747,748],{"class":91}," ProcessPoolExecutor(",[81,750,751],{"class":184},"max_workers",[81,753,121],{"class":87},[81,755,664],{"class":124},[81,757,667],{"class":91},[81,759,430],{"class":87},[81,761,762],{"class":91}," ex:\n",[81,764,765,767,769,772,775,777,779,781,783],{"class":83,"line":115},[81,766,680],{"class":91},[81,768,121],{"class":87},[81,770,771],{"class":124}," list",[81,773,774],{"class":91},"(ex.map(cpu_work, [",[81,776,688],{"class":124},[81,778,468],{"class":91},[81,780,471],{"class":87},[81,782,695],{"class":124},[81,784,785],{"class":91},"))\n",[15,787,788,790,791,793,794,797],{},[19,789,554],{}," reach for ",[62,792,25],{}," (or ",[62,795,796],{},"ProcessPoolExecutor",") when a single\nCPU core is the bottleneck — profiling should show near-100% CPU usage on one core.",[557,799,800],{},[15,801,561,802],{},[563,803,805],{"href":804},"\u002Fpython\u002Fconcurrency\u002Fmultiprocessing","Multiprocessing interview questions",[10,807,809,811],{"id":808},"asyncio-thousands-of-concurrent-io-operations",[62,810,29],{}," — thousands of concurrent I\u002FO operations",[15,813,814,816,817,820,821,824,825,828,829,831,832,835],{},[62,815,29],{}," is Python's framework for ",[19,818,819],{},"cooperative concurrency on a single thread",". An\n",[19,822,823],{},"event loop"," runs many ",[19,826,827],{},"coroutines"," that voluntarily yield control with ",[62,830,328],{}," when\nthey're waiting for I\u002FO. Because there's no thread switching overhead and coroutines are\ncheap objects, asyncio can manage ",[19,833,834],{},"tens of thousands of concurrent connections"," — ideal\nfor web servers, chat systems, and API gateway code.",[72,837,839],{"className":74,"code":838,"language":76,"meta":77,"style":77},"import asyncio, aiohttp\n\nasync def fetch(session, url):\n    async with session.get(url) as r:\n        return await r.text()     # yields to event loop while waiting\n\nasync def main():\n    async with aiohttp.ClientSession() as session:\n        tasks = [fetch(session, \"https:\u002F\u002Fexample.com\") for _ in range(1000)]\n        pages = await asyncio.gather(*tasks)   # all 1000 requests overlap\n        print(len(pages))\n\nasyncio.run(main())\n",[62,840,841,848,852,865,880,893,897,908,922,952,972,985,989],{"__ignoreMap":77},[81,842,843,845],{"class":83,"line":84},[81,844,88],{"class":87},[81,846,847],{"class":91}," asyncio, aiohttp\n",[81,849,850],{"class":83,"line":95},[81,851,99],{"emptyLinePlaceholder":98},[81,853,854,857,860,862],{"class":83,"line":102},[81,855,856],{"class":87},"async",[81,858,859],{"class":87}," def",[81,861,416],{"class":108},[81,863,864],{"class":91},"(session, url):\n",[81,866,867,870,873,876,878],{"class":83,"line":115},[81,868,869],{"class":87},"    async",[81,871,872],{"class":87}," with",[81,874,875],{"class":91}," session.get(url) ",[81,877,430],{"class":87},[81,879,433],{"class":91},[81,881,882,884,887,890],{"class":83,"line":128},[81,883,438],{"class":87},[81,885,886],{"class":87}," await",[81,888,889],{"class":91}," r.text()     ",[81,891,892],{"class":164},"# yields to event loop while waiting\n",[81,894,895],{"class":83,"line":152},[81,896,99],{"emptyLinePlaceholder":98},[81,898,899,901,903,906],{"class":83,"line":168},[81,900,856],{"class":87},[81,902,859],{"class":87},[81,904,905],{"class":108}," main",[81,907,112],{"class":91},[81,909,910,912,914,917,919],{"class":83,"line":173},[81,911,869],{"class":87},[81,913,872],{"class":87},[81,915,916],{"class":91}," aiohttp.ClientSession() ",[81,918,430],{"class":87},[81,920,921],{"class":91}," session:\n",[81,923,924,927,929,932,934,936,938,940,942,944,946,949],{"class":83,"line":193},[81,925,926],{"class":91},"        tasks ",[81,928,121],{"class":87},[81,930,931],{"class":91}," [fetch(session, ",[81,933,465],{"class":464},[81,935,667],{"class":91},[81,937,502],{"class":87},[81,939,134],{"class":91},[81,941,137],{"class":87},[81,943,140],{"class":124},[81,945,143],{"class":91},[81,947,948],{"class":124},"1000",[81,950,951],{"class":91},")]\n",[81,953,954,957,959,961,964,966,969],{"class":83,"line":209},[81,955,956],{"class":91},"        pages ",[81,958,121],{"class":87},[81,960,886],{"class":87},[81,962,963],{"class":91}," asyncio.gather(",[81,965,471],{"class":87},[81,967,968],{"class":91},"tasks)   ",[81,970,971],{"class":164},"# all 1000 requests overlap\n",[81,973,974,977,979,982],{"class":83,"line":215},[81,975,976],{"class":124},"        print",[81,978,143],{"class":91},[81,980,981],{"class":124},"len",[81,983,984],{"class":91},"(pages))\n",[81,986,987],{"class":83,"line":221},[81,988,99],{"emptyLinePlaceholder":98},[81,990,992],{"class":83,"line":991},13,[81,993,994],{"class":91},"asyncio.run(main())\n",[15,996,997,998,1001,1002,22,1005,1008,1009,1012,1013,1016,1017,22,1020,1023,1024,1027],{},"The critical rule: ",[19,999,1000],{},"never block the event loop",". Any blocking call (a synchronous\n",[62,1003,1004],{},"requests.get",[62,1006,1007],{},"time.sleep",", heavy CPU work) freezes ",[67,1010,1011],{},"all"," coroutines until it\ncompletes. Use ",[62,1014,1015],{},"await asyncio.sleep",", async libraries (",[62,1018,1019],{},"aiohttp",[62,1021,1022],{},"asyncpg","), and\n",[62,1025,1026],{},"loop.run_in_executor"," for blocking operations you can't avoid.",[15,1029,1030,1032,1033,1035,1036,1039],{},[19,1031,554],{}," choose ",[62,1034,29],{}," when you need ",[19,1037,1038],{},"massive I\u002FO concurrency"," (hundreds\nto thousands of simultaneous connections) and are willing to use async-compatible\nlibraries throughout.",[557,1041,1042],{},[15,1043,561,1044],{},[563,1045,1047],{"href":1046},"\u002Fpython\u002Fconcurrency\u002Fasyncio","asyncio & async\u002Fawait interview questions",[10,1049,1051],{"id":1050},"the-decision-flowchart","The decision flowchart",[72,1053,1058],{"className":1054,"code":1056,"language":1057},[1055],"language-text","Is your bottleneck I\u002FO (network, disk, database)?\n├── Yes → How many concurrent operations?\n│         ├── Dozens → threading  (simple, familiar)\n│         └── Hundreds \u002F thousands → asyncio  (scalable, low overhead)\n└── No → Is it CPU computation?\n          └── Yes → multiprocessing \u002F ProcessPoolExecutor  (real parallelism)\n","text",[62,1059,1056],{"__ignoreMap":77},[15,1061,1062,1063],{},"Or as a single question: ",[19,1064,1065],{},"\"What am I waiting on?\"",[1067,1068,1069,1076],"ul",{},[1070,1071,1072,1075],"li",{},[19,1073,1074],{},"Waiting on the network \u002F disk"," → threading or asyncio",[1070,1077,1078,1081],{},[19,1079,1080],{},"Waiting on the CPU"," → multiprocessing",[10,1083,1085,1086,1089],{"id":1084},"when-concurrentfutures-simplifies-the-choice","When ",[62,1087,1088],{},"concurrent.futures"," simplifies the choice",[15,1091,1092,1094],{},[62,1093,1088],{}," provides a unified API over both threading and multiprocessing:",[72,1096,1098],{"className":74,"code":1097,"language":76,"meta":77,"style":77},"from concurrent.futures import ThreadPoolExecutor, ProcessPoolExecutor\n\n# I\u002FO-bound: swap ThreadPoolExecutor for ProcessPoolExecutor and back easily\nwith ThreadPoolExecutor(max_workers=10) as ex:\n    results = list(ex.map(fetch_url, urls))\n\nwith ProcessPoolExecutor(max_workers=4) as ex:\n    results = list(ex.map(cpu_work, data))\n",[62,1099,1100,1111,1115,1120,1140,1151,1155,1173],{"__ignoreMap":77},[81,1101,1102,1104,1106,1108],{"class":83,"line":84},[81,1103,592],{"class":87},[81,1105,732],{"class":91},[81,1107,88],{"class":87},[81,1109,1110],{"class":91}," ThreadPoolExecutor, ProcessPoolExecutor\n",[81,1112,1113],{"class":83,"line":95},[81,1114,99],{"emptyLinePlaceholder":98},[81,1116,1117],{"class":83,"line":102},[81,1118,1119],{"class":164},"# I\u002FO-bound: swap ThreadPoolExecutor for ProcessPoolExecutor and back easily\n",[81,1121,1122,1124,1127,1129,1131,1134,1136,1138],{"class":83,"line":115},[81,1123,653],{"class":87},[81,1125,1126],{"class":91}," ThreadPoolExecutor(",[81,1128,751],{"class":184},[81,1130,121],{"class":87},[81,1132,1133],{"class":124},"10",[81,1135,667],{"class":91},[81,1137,430],{"class":87},[81,1139,762],{"class":91},[81,1141,1142,1144,1146,1148],{"class":83,"line":128},[81,1143,680],{"class":91},[81,1145,121],{"class":87},[81,1147,771],{"class":124},[81,1149,1150],{"class":91},"(ex.map(fetch_url, urls))\n",[81,1152,1153],{"class":83,"line":152},[81,1154,99],{"emptyLinePlaceholder":98},[81,1156,1157,1159,1161,1163,1165,1167,1169,1171],{"class":83,"line":168},[81,1158,653],{"class":87},[81,1160,748],{"class":91},[81,1162,751],{"class":184},[81,1164,121],{"class":87},[81,1166,664],{"class":124},[81,1168,667],{"class":91},[81,1170,430],{"class":87},[81,1172,762],{"class":91},[81,1174,1175,1177,1179,1181],{"class":83,"line":173},[81,1176,680],{"class":91},[81,1178,121],{"class":87},[81,1180,771],{"class":124},[81,1182,1183],{"class":91},"(ex.map(cpu_work, data))\n",[15,1185,1186,1187,1190,1191,1194],{},"Use it when you want straightforward ",[62,1188,1189],{},"map","\u002F",[62,1192,1193],{},"submit"," semantics without managing threads or\nprocesses manually.",[557,1196,1197],{},[15,1198,561,1199],{},[563,1200,1202],{"href":1201},"\u002Fpython\u002Fconcurrency\u002Fconcurrent-futures","concurrent.futures interview questions",[10,1204,1206],{"id":1205},"recap","Recap",[15,1208,1209,1212,1213,1216,1217,1219],{},[19,1210,1211],{},"Threading"," overlaps blocking I\u002FO by letting threads wait simultaneously — the GIL\nmeans no CPU-level parallelism, but I\u002FO-heavy code still gets a real speedup. ",[19,1214,1215],{},"Multiprocessing","\nspawns separate processes with their own GIL, giving true multi-core parallelism for\nCPU-bound work at the cost of higher overhead and pickling constraints. ",[19,1218,29],{}," runs\nall coroutines on one thread in a single event loop, enabling tens of thousands of\nconcurrent I\u002FO operations with minimal overhead — but every line in the async path must\nyield control properly. The deciding factor is always: I\u002FO bottleneck (threading or\nasyncio) vs CPU bottleneck (multiprocessing).",[1221,1222,1223],"style",{},"html pre.shiki code .szBVR, html code.shiki .szBVR{--shiki-default:#D73A49;--shiki-dark:#F97583}html pre.shiki code .sVt8B, html code.shiki .sVt8B{--shiki-default:#24292E;--shiki-dark:#E1E4E8}html pre.shiki code .sScJk, html code.shiki .sScJk{--shiki-default:#6F42C1;--shiki-dark:#B392F0}html pre.shiki code .sj4cs, html code.shiki .sj4cs{--shiki-default:#005CC5;--shiki-dark:#79B8FF}html pre.shiki code .sJ8bj, html code.shiki .sJ8bj{--shiki-default:#6A737D;--shiki-dark:#6A737D}html pre.shiki code .s4XuR, html code.shiki .s4XuR{--shiki-default:#E36209;--shiki-dark:#FFAB70}html .default .shiki span {color: var(--shiki-default);background: var(--shiki-default-bg);font-style: var(--shiki-default-font-style);font-weight: var(--shiki-default-font-weight);text-decoration: var(--shiki-default-text-decoration);}html .shiki span {color: var(--shiki-default);background: var(--shiki-default-bg);font-style: var(--shiki-default-font-style);font-weight: var(--shiki-default-font-weight);text-decoration: var(--shiki-default-text-decoration);}html .dark .shiki span {color: var(--shiki-dark);background: var(--shiki-dark-bg);font-style: var(--shiki-dark-font-style);font-weight: var(--shiki-dark-font-weight);text-decoration: var(--shiki-dark-text-decoration);}html.dark .shiki span {color: var(--shiki-dark);background: var(--shiki-dark-bg);font-style: var(--shiki-dark-font-style);font-weight: var(--shiki-dark-font-weight);text-decoration: var(--shiki-dark-text-decoration);}html pre.shiki code .sZZnC, html code.shiki .sZZnC{--shiki-default:#032F62;--shiki-dark:#9ECBFF}",{"title":77,"searchDepth":95,"depth":95,"links":1225},[1226,1227,1228,1229,1231,1233,1235,1236,1238],{"id":12,"depth":95,"text":13},{"id":41,"depth":95,"text":42},{"id":233,"depth":95,"text":234},{"id":383,"depth":95,"text":1230},"threading — overlapping I\u002FO waits",{"id":569,"depth":95,"text":1232},"multiprocessing — true CPU parallelism",{"id":808,"depth":95,"text":1234},"asyncio — thousands of concurrent I\u002FO operations",{"id":1050,"depth":95,"text":1051},{"id":1084,"depth":95,"text":1237},"When concurrent.futures simplifies the choice",{"id":1205,"depth":95,"text":1206},"Python threading vs multiprocessing vs asyncio explained — the GIL, I\u002FO-bound vs CPU-bound work, when each model helps and when it doesn't, and the decision rule engineers use in interviews and production.","hard","md","Python",{},"\u002Fblog\u002Fpython-threading-vs-multiprocessing-vs-asyncio",{"title":5,"description":1239},"blog\u002Fpython-threading-vs-multiprocessing-vs-asyncio","Threading vs Multiprocessing vs asyncio","Concurrency & Parallelism","concurrency","2026-06-21","9vhyXCXigDeMgmZwkTxiKjZ2ZxvvUGo1fMkPCTR3enw",1782244087984]