socialgekon.com
  • Glavni
  • Ljudje In Ekipe
  • Objava
  • Ui Design
  • Postopek Oblikovanja
Back-End

Vadnica za večnitnost in večprocesorsko obdelavo Pythona

Opomba: Na splošno zahtevo, da predstavim nekaj alternativnih tehnik --- vključno z async / await, ki so na voljo samo od pojava Pythona 3.5 --- sem dodal nekaj posodobitev na koncu članka . Uživajte!

Razprave, ki kritizirajo Python, pogosto govorijo o tem, kako težko je uporabljati Python za večnitno delo, s prstom kažejo na tako imenovano globalno zaklepanje tolmačev (ljubkovalno imenovano GIL ), ki preprečuje istočasno izvajanje več niti kode Python. Zaradi tega se večnitni modul Python ne obnaša povsem tako, kot bi pričakovali, če niste Razvijalec Python in prihajate iz drugih jezikov, kot sta C ++ ali Java. Jasno je treba poudariti, da lahko v Pythonu še vedno pišemo kodo, ki deluje hkrati ali vzporedno, in močno vplivamo na rezultat, če upoštevamo nekatere stvari. Če ga še niste prebrali, predlagam, da si ogledate Eqbal Quran's članek o sočasnosti in vzporednosti v Rubyju tukaj na spletnem dnevniku ApeeScape Engineering.

V tej vadnici za sočasnost Pythona bomo napisali majhen skript za Python, da bomo lahko prenesli najbolj priljubljene slike iz Imgurja. Začeli bomo z različico, ki slike prenaša zaporedno ali eno za drugo. Kot predpogoj se boste morali registrirati prijava na Imgurju . Če že nimate računa Imgur, ga najprej ustvarite.

Skripte v teh primerih navojev so bile preizkušene s Pythonom 3.6.4. Z nekaterimi spremembami bi se morali izvajati tudi s Pythonom 2 - urllib je tisto, kar se je najbolj spremenilo med tema dvema različicama Pythona.



Začetek uporabe Python Multithreading

Začnimo z ustvarjanjem modula Python z imenom download.py. Ta datoteka bo vsebovala vse funkcije, potrebne za pridobitev seznama slik in njihovo nalaganje. Te funkcionalnosti bomo razdelili na tri ločene funkcije:

  • get_links
  • download_link
  • setup_download_dir

Tretja funkcija, setup_download_dir, bo uporabljena za ustvarjanje ciljnega imenika za prenos, če ta še ne obstaja.

Imgurjev API zahteva, da zahteve HTTP vsebujejo Authorization glava z ID-jem odjemalca. Ta ID odjemalca najdete na nadzorni plošči aplikacije, ki ste jo registrirali na Imgurju, odgovor pa bo kodiran v obliki JSON. Za njegovo dekodiranje lahko uporabimo Pythonovo standardno knjižnico JSON. Prenos slike je še enostavnejša naloga, saj je vse, kar morate storiti, da sliko po njenem URL-ju zapišete v datoteko.

Takole je videti skript:

import json import logging import os from pathlib import Path from urllib.request import urlopen, Request logger = logging.getLogger(__name__) types = {'image/jpeg', 'image/png'} def get_links(client_id): headers = {'Authorization': 'Client-ID {}'.format(client_id)} req = Request('https://api.imgur.com/3/gallery/random/random/', headers=headers, method='GET') with urlopen(req) as resp: data = json.loads(resp.read().decode('utf-8')) return [item['link'] for item in data['data'] if 'type' in item and item['type'] in types] def download_link(directory, link): download_path = directory / os.path.basename(link) with urlopen(link) as image, download_path.open('wb') as f: f.write(image.read()) logger.info('Downloaded %s', link) def setup_download_dir(): download_dir = Path('images') if not download_dir.exists(): download_dir.mkdir() return download_dir

Nato bomo morali napisati modul, ki bo te funkcije uporabljal za prenos slik, eno za drugo. To bomo poimenovali single.py. Ta bo vsebovala glavno funkcijo naše prve, naivne različice programa za prenos slik Imgur. Modul bo pridobil ID odjemalca Imgur v spremenljivki okolja IMGUR_CLIENT_ID. Priklicalo bo setup_download_dir da ustvarite ciljni imenik za prenos. Končno bo pokazal seznam slik z get_links funkcijo, filtrirajte vse URL-je GIF in albumov ter nato uporabite download_link da naložite in shranite vsako od teh slik na disk. Tukaj je single.py izgleda kot:

import logging import os from time import time from download import setup_download_dir, get_links, download_link logging.basicConfig(level=logging.INFO, format='%(asctime)s - %(name)s - %(levelname)s - %(message)s') logger = logging.getLogger(__name__) def main(): ts = time() client_id = os.getenv('IMGUR_CLIENT_ID') if not client_id: raise Exception('Couldn't find IMGUR_CLIENT_ID environment variable!') download_dir = setup_download_dir() links = get_links(client_id) for link in links: download_link(download_dir, link) logging.info('Took %s seconds', time() - ts) if __name__ == '__main__': main()

V mojem prenosniku je ta skript trajal 19,4 sekunde, da je prenesel 91 slik. Upoštevajte, da se te številke lahko razlikujejo glede na omrežje, v katerem ste. 19,4 sekunde ni strašno dolgo, a kaj, če bi želeli prenesti več slik? Morda 900 slik, namesto 90. S povprečno 0,2 sekunde na sliko bi 900 slik trajalo približno 3 minute. Za 9000 slik bi trajalo 30 minut. Dobra novica je, da lahko z uvedbo sočasnosti ali vzporednosti to močno pospešimo.

Vsi nadaljnji primeri kode bodo prikazali samo izjave o uvozu, ki so nove in specifične za te primere. Za udobje lahko vse te skripte Python najdete v to skladišče GitHub .

Sočasnost in paralelizem v Pythonu: primer navojev

Threading je eden najbolj znanih pristopov k doseganju Pythonove sočasnosti in vzporednosti. Threading je funkcija, ki jo običajno zagotavlja operacijski sistem. Niti so lažji od procesov in imajo isti pomnilniški prostor.

Python večnitni pomnilniški model

V tem primeru navojev navojev Python bomo napisali nov modul, ki bo nadomestil single.py Ta modul bo ustvaril skupino osmih niti, kar bo skupaj devet glavnih niti, vključno z glavno nitjo. Izbral sem osem delovnih niti, ker ima moj računalnik osem jeder procesorja in ena delovna nit na jedro se je zdela dobra številka za to, koliko niti naj teče hkrati. V praksi je to število izbrano veliko bolj skrbno glede na druge dejavnike, na primer druge aplikacije in storitve, ki se izvajajo na istem računalniku.

Ta je skoraj enak prejšnjemu, z izjemo, da imamo zdaj nov razred, DownloadWorker, ki je potomec Pythona Thread razred. Preglašena je bila metoda zagon, ki izvaja neskončno zanko. Pri vsaki ponovitvi pokliče self.queue.get() da poskusite pridobiti URL do iz čakalne vrste, ki je varna za nit. Blokira, dokler v čakalni vrsti ni elementa, ki ga mora delavec obdelati. Ko delavec prejme element iz čakalne vrste, nato pokliče isto download_link metoda, ki je bila uporabljena v prejšnjem skriptu za prenos slike v imenik slik. Po končanem prenosu delavec signalizira čakalni vrsti, da je ta naloga opravljena. To je zelo pomembno, saj čakalna vrsta spremlja, koliko nalog je bilo postavljenih v vrsto. Klic na queue.join() bi za vedno blokiral glavno nit, če delavci ne bi signalizirali, da so opravili nalogo.

import logging import os from queue import Queue from threading import Thread from time import time from download import setup_download_dir, get_links, download_link logging.basicConfig(level=logging.INFO, format='%(asctime)s - %(name)s - %(levelname)s - %(message)s') logger = logging.getLogger(__name__) class DownloadWorker(Thread): def __init__(self, queue): Thread.__init__(self) self.queue = queue def run(self): while True: # Get the work from the queue and expand the tuple directory, link = self.queue.get() try: download_link(directory, link) finally: self.queue.task_done() def main(): ts = time() client_id = os.getenv('IMGUR_CLIENT_ID') if not client_id: raise Exception('Couldn't find IMGUR_CLIENT_ID environment variable!') download_dir = setup_download_dir() links = get_links(client_id) # Create a queue to communicate with the worker threads queue = Queue() # Create 8 worker threads for x in range(8): worker = DownloadWorker(queue) # Setting daemon to True will let the main thread exit even though the workers are blocking worker.daemon = True worker.start() # Put the tasks into the queue as a tuple for link in links: logger.info('Queueing {}'.format(link)) queue.put((download_dir, link)) # Causes the main thread to wait for the queue to finish processing all the tasks queue.join() logging.info('Took %s', time() - ts) if __name__ == '__main__': main()

Zagon tega primera skripta navojev Python na istem računalniku, ki je bil uporabljen prej, povzroči prenos časa 4,1 sekunde! To je 4,7-krat hitreje kot v prejšnjem primeru. Čeprav je to veliko hitrejše, je treba omeniti, da se je ves čas tega postopka zaradi GIL izvajala le ena nit. Zato je ta koda sočasna, vendar ne vzporedna. Razlog, da je še vedno hitrejši, je, da je to naloga, vezana na IO. Procesor se med prenosom teh slik skoraj ne znoji in večino časa porabi za čakanje na omrežje. To je razlog, zakaj lahko večnitnost Python poveča hitrost. Procesor lahko preklaplja med nitmi, kadar je katera od njih pripravljena za nekaj dela. Uporaba modula navojev v Pythonu ali katerem koli drugem interpretiranem jeziku z GIL lahko dejansko povzroči manjšo zmogljivost. Če vaša koda izvaja nalogo, vezano na CPU, na primer razpakiranje datotek gzip, z uporabo threading modul bo povzročil počasnejši čas izvajanja. Za naloge, vezane na CPU, in resnično vzporedno izvajanje lahko uporabimo večprocesorski modul.

Medtem ko de facto sklic na izvedbo Pythona - CPython - ima GIL, to ne velja za vse izvedbe Pythona. Na primer, IronPython, izvedba Pythona, ki uporablja ogrodje .NET, nima GIL, prav tako pa tudi Jython, ki temelji na Javi. Tu lahko najdete seznam delujočih implementacij Pythona tukaj .

Sorodno: Najboljši postopki in nasveti za Python razvijalcev ApeeScape

Sočasnost in vzporednost v Pythonu Primer 2: Drstenje več procesov

Modul za večprocesorsko obdelavo je lažje vstopiti kot modul za navoja, saj nam ni treba dodati razreda, kot je primer navojev Python. Edine spremembe, ki jih moramo narediti, so v glavni funkciji.

Vadnica za večprocesorsko obdelavo Pythona: Moduli

Za uporabo več procesov ustvarimo večprocesorsko Pool. Z metodo map, ki jo ponuja, bomo seznam URL-jev poslali v bazen, ta pa bo ustvaril osem novih procesov in uporabil vsakega za vzporedni prenos slik. To je resnična paralelizem, vendar to prinaša stroške. Celoten pomnilnik skripta se kopira v vsak podproces, ki se ustvari. V tem preprostem primeru ni veliko, vendar lahko za netrivialne programe zlahka postane režija.

import logging import os from functools import partial from multiprocessing.pool import Pool from time import time from download import setup_download_dir, get_links, download_link logging.basicConfig(level=logging.DEBUG, format='%(asctime)s - %(name)s - %(levelname)s - %(message)s') logging.getLogger('requests').setLevel(logging.CRITICAL) logger = logging.getLogger(__name__) def main(): ts = time() client_id = os.getenv('IMGUR_CLIENT_ID') if not client_id: raise Exception('Couldn't find IMGUR_CLIENT_ID environment variable!') download_dir = setup_download_dir() links = get_links(client_id) download = partial(download_link, download_dir) with Pool(4) as p: p.map(download, links) logging.info('Took %s seconds', time() - ts) if __name__ == '__main__': main()

Sočasnost in vzporednost v Pythonu Primer 3: Razdeljevanje več delavcem

Čeprav so moduli za navojev in večprocesorsko obdelavo odlični za skripte, ki se izvajajo v vašem osebnem računalniku, kaj storiti, če želite, da se delo opravi na drugem računalniku ali če morate povečati število procesorjev na več kot en CPU ročaj? Odličen primer tega so dolgotrajna zaledna opravila za spletne aplikacije. Če imate nekaj dolgotrajnih nalog, ne želite na istem računalniku zasukati kopice podprocesov ali niti, ki morajo izvajati preostalo kodo aplikacije. To bo poslabšalo delovanje vaše aplikacije za vse vaše uporabnike. Odlično bi bilo, če bi lahko ta opravila izvajali na drugem ali mnogih drugih strojih.

Odlična knjižnica Python za to nalogo je RQ , zelo preprosta, a zmogljiva knjižnica. Funkcijo in njene argumente najprej uvrstite v knjižnico. To kumarice predstavitev klica funkcije, ki je nato dodana a Redis seznam. Prvi korak je čakanje na čakalno vrsto, vendar še ničesar ne boste storili. Prav tako potrebujemo vsaj enega delavca, ki bo prisluhnil tej čakalni vrsti.

Model knjižnice čakalnih vrst RQ Python

Prvi korak je namestitev in zagon strežnika Redis v računalniku ali dostop do delujočega strežnika Redis. Po tem je le nekaj majhnih sprememb obstoječe kode. Najprej izdelamo primerek RQ Queue in mu posredujemo primerek strežnika Redis iz knjižnica redis-py . Potem, namesto da samo pokličete našo download_link metoda, imenujemo q.enqueue(download_link, download_dir, link). Metoda čakalne vrste sprejme funkcijo kot prvi argument, nato pa se kateri koli drugi argumenti ali argumenti ključnih besed posredujejo tej funkciji, ko je opravilo dejansko izvedeno.

Zadnji korak, ki ga moramo narediti, je zagon nekaterih delavcev. RQ ponuja priročen skript za zagon delavcev v privzeti čakalni vrsti. Samo zaženite rqworker v terminalskem oknu in bo zagnal delavca, ki posluša v privzeti vrsti. Prepričajte se, da je vaš trenutni delovni imenik enak tistemu, v katerem so skripti. Če želite poslušati drugo vrsto, lahko zaženete rqworker queue_name in poslušalo bo to imenovano vrsto. Odlična stvar pri RQ je, da dokler se lahko povežete z Redisom, lahko na poljubnih različnih strojih izvajate toliko delavcev, kot želite; zato ga je zelo enostavno povečati, ko raste vaša aplikacija. Tu je vir za različico RQ:

import logging import os from redis import Redis from rq import Queue from download import setup_download_dir, get_links, download_link logging.basicConfig(level=logging.DEBUG, format='%(asctime)s - %(name)s - %(levelname)s - %(message)s') logging.getLogger('requests').setLevel(logging.CRITICAL) logger = logging.getLogger(__name__) def main(): client_id = os.getenv('IMGUR_CLIENT_ID') if not client_id: raise Exception('Couldn't find IMGUR_CLIENT_ID environment variable!') download_dir = setup_download_dir() links = get_links(client_id) q = Queue(connection=Redis(host='localhost', port=6379)) for link in links: q.enqueue(download_link, download_dir, link) if __name__ == '__main__': main()

Vendar RQ ni edina rešitev čakalne vrste Python. RQ je enostaven za uporabo in izjemno dobro pokriva primere enostavne uporabe, če pa so potrebne naprednejše možnosti, druge rešitve čakalne vrste Python 3 (kot je npr. Zelena ) je lahko uporabljen.

Python Multithreading vs Multiprocessing

Če je vaša koda vezana na IO, bosta večprocesorska in večnitna obdelava v Pythonu delovala za vas. Večprocesorsko obdelavo je lažje vstaviti kot navoj, vendar ima večji režijski pomnilnik. Če je vaša koda vezana na CPU, bo najverjetneje boljša izbira večprocesorska obdelava - še posebej, če ima ciljni računalnik več jeder ali CPU-jev. Za spletne aplikacije in ko boste morali delo prilagoditi več strojem, bo RQ boljši za vas.

Sorodno: Postanite bolj napredni: izogibajte se 10 najpogostejšim napakam programerjev Python

Nadgradnja

Python concurrent.futures

Nekaj ​​novega od Pythona 3.2, česar se prvotni članek ni dotaknil, je concurrent.futures paket. Ta paket ponuja še en način za uporabo sočasnosti in vzporednosti s Pythonom.

V prvotnem članku sem omenil, da bi Pythonov večprocesorski modul lažje spustil v obstoječo kodo kot modul za navoje. To je bilo zato, ker je modul navojev Python 3 zahteval podrazvrstitev Thread razred in tudi ustvarjanje Queue za niti, ki jih lahko spremljate za delo.

Uporaba a concurrent.futures.ThreadPoolExecutor naredi primer kode navojev Python skoraj enak večprocesorskemu modulu.

import logging import os from concurrent.futures import ThreadPoolExecutor from functools import partial from time import time from download import setup_download_dir, get_links, download_link logging.basicConfig(level=logging.INFO, format='%(asctime)s - %(name)s - %(levelname)s - %(message)s') logger = logging.getLogger(__name__) def main(): client_id = os.getenv('IMGUR_CLIENT_ID') if not client_id: raise Exception('Couldn't find IMGUR_CLIENT_ID environment variable!') download_dir = setup_download_dir() links = get_links(client_id) # By placing the executor inside a with block, the executors shutdown method # will be called cleaning up threads. # # By default, the executor sets number of workers to 5 times the number of # CPUs. with ThreadPoolExecutor() as executor: # Create a new partially applied function that stores the directory # argument. # # This allows the download_link function that normally takes two # arguments to work with the map function that expects a function of a # single argument. fn = partial(download_link, download_dir) # Executes fn concurrently using threads on the links iterable. The # timeout is for the entire process, not a single call, so downloading # all images must complete within 30 seconds. executor.map(fn, links, timeout=30) if __name__ == '__main__': main()

Zdaj, ko smo vse te slike prenesli z našim Pythonom ThreadPoolExecutor, jih lahko uporabimo za testiranje naloge, vezane na CPU. Ustvarimo lahko sličice različic vseh slik v enonitnem skriptu z enim procesom in nato preizkusimo rešitev, ki temelji na več obdelavah.

Uporabili bomo Vzglavnik knjižnico za obdelavo velikosti slik.

Tu je naš začetni skript.

import logging from pathlib import Path from time import time from PIL import Image logging.basicConfig(level=logging.INFO, format='%(asctime)s - %(name)s - %(levelname)s - %(message)s') logger = logging.getLogger(__name__) def create_thumbnail(size, path): ''' Creates a thumbnail of an image with the same name as image but with _thumbnail appended before the extension. E.g.: >>> create_thumbnail((128, 128), 'image.jpg') A new thumbnail image is created with the name image_thumbnail.jpg :param size: A tuple of the width and height of the image :param path: The path to the image file :return: None ''' image = Image.open(path) image.thumbnail(size) path = Path(path) name = path.stem + '_thumbnail' + path.suffix thumbnail_path = path.with_name(name) image.save(thumbnail_path) def main(): ts = time() for image_path in Path('images').iterdir(): create_thumbnail((128, 128), image_path) logging.info('Took %s', time() - ts) if __name__ == '__main__': main()

Ta skript ponovi poti v images mapo in za vsako pot zažene funkcijo create_thumbnail. Ta funkcija uporablja vzglavnik, da odpre sliko, ustvari sličico in shrani novo, manjšo sliko z istim imenom kot izvirnik, vendar z _thumbnail priloženo k imenu.

Zagon tega skripta na 160 slikah v skupni vrednosti 36 milijonov traja 2,32 sekunde. Poglejmo, ali lahko to pospešimo z uporabo ProcessPoolExecutor .

import logging from pathlib import Path from time import time from functools import partial from concurrent.futures import ProcessPoolExecutor from PIL import Image logging.basicConfig(level=logging.INFO, format='%(asctime)s - %(name)s - %(levelname)s - %(message)s') logger = logging.getLogger(__name__) def create_thumbnail(size, path): ''' Creates a thumbnail of an image with the same name as image but with _thumbnail appended before the extension. E.g.: >>> create_thumbnail((128, 128), 'image.jpg') A new thumbnail image is created with the name image_thumbnail.jpg :param size: A tuple of the width and height of the image :param path: The path to the image file :return: None ''' path = Path(path) name = path.stem + '_thumbnail' + path.suffix thumbnail_path = path.with_name(name) image = Image.open(path) image.thumbnail(size) image.save(thumbnail_path) def main(): ts = time() # Partially apply the create_thumbnail method, setting the size to 128x128 # and returning a function of a single argument. thumbnail_128 = partial(create_thumbnail, (128, 128)) # Create the executor in a with block so shutdown is called when the block # is exited. with ProcessPoolExecutor() as executor: executor.map(thumbnail_128, Path('images').iterdir()) logging.info('Took %s', time() - ts) if __name__ == '__main__': main()

The create_thumbnail metoda je enaka zadnji skripti. Glavna razlika je v ustvarjanju ProcessPoolExecutor. Izvršiteljev zemljevid metoda se uporablja za vzporedno ustvarjanje sličic. Privzeto je ProcessPoolExecutor ustvari en podproces na CPU. Zagon tega skripta na istih 160 slikah je trajal 1,05 sekunde - 2,2-krat hitreje!

Async / Await (samo Python 3.5+)

Eden najbolj zahtevanih elementov v komentarjih na izvirni članek je bil na primer uporaba Pythona 3 asincio modul. V primerjavi z drugimi primeri obstaja nekaj novih sintaks Python, ki so morda za večino ljudi nove in tudi nekateri novi koncepti. Nesrečno dodatno plast zapletenosti povzroča Pythonova vgrajena urllib modul ni asinhron. Za popolne prednosti asyncia bomo morali uporabiti async knjižnico HTTP. Za to bomo uporabili aiohttp .

Skočimo naravnost v kodo in sledila bo podrobnejša razlaga.

import asyncio import logging import os from time import time import aiohttp from download import setup_download_dir, get_links logging.basicConfig(level=logging.INFO, format='%(asctime)s - %(name)s - %(levelname)s - %(message)s') logger = logging.getLogger(__name__) async def async_download_link(session, directory, link): ''' Async version of the download_link method we've been using in the other examples. :param session: aiohttp ClientSession :param directory: directory to save downloads :param link: the url of the link to download :return: ''' download_path = directory / os.path.basename(link) async with session.get(link) as response: with download_path.open('wb') as f: while True: # await pauses execution until the 1024 (or less) bytes are read from the stream chunk = await response.content.read(1024) if not chunk: # We are done reading the file, break out of the while loop break f.write(chunk) logger.info('Downloaded %s', link) # Main is now a coroutine async def main(): client_id = os.getenv('IMGUR_CLIENT_ID') if not client_id: raise Exception('Couldn't find IMGUR_CLIENT_ID environment variable!') download_dir = setup_download_dir() # We use a session to take advantage of tcp keep-alive # Set a 3 second read and connect timeout. Default is 5 minutes async with aiohttp.ClientSession(conn_timeout=3, read_timeout=3) as session: tasks = [(async_download_link(session, download_dir, l)) for l in get_links(client_id)] # gather aggregates all the tasks and schedules them in the event loop await asyncio.gather(*tasks, return_exceptions=True) if __name__ == '__main__': ts = time() # Create the asyncio event loop loop = asyncio.get_event_loop() try: loop.run_until_complete(main()) finally: # Shutdown the loop even if there is an exception loop.close() logger.info('Took %s seconds to complete', time() - ts)

Tu je treba kar nekaj razpakirati. Začnimo z glavno vstopno točko programa. Prva nova stvar, ki jo naredimo z modulom asyncio, je pridobiti zanko dogodka. Zanka dogodkov obravnava vso asinhrono kodo. Nato se zanka zažene, dokler ni dokončana in posreduje main funkcijo. V definiciji main je del nove sintakse: async def. Opazili boste tudi await in with async.

Sintaksa async / await je bila uvedena v PEP492 . The async def sintaksa označuje funkcijo kot koroutina . Notranji programi temeljijo na Pythonovih generatorjih, vendar niso povsem ista stvar. Programi vrnejo podprogramski objekt, podobno kot generatorji vrnejo generatorski objekt. Ko dobite podprogram, rezultate dobite z await izraz. Ko podprogram pokliče await, se izvajanje podprograma začasno ustavi, dokler se pričakovano ne konča. Ta prekinitev omogoča dokončanje drugega dela, medtem ko je koroutina začasno ustavljena, 'v čakanju' na nek rezultat. Na splošno bo to rezultat neke vrste V / I, kot je zahteva za bazo podatkov ali v našem primeru zahteva HTTP.

The download_link funkcijo je bilo treba precej spremeniti. Prej smo se zanašali na urllib da bomo za nas prebrali breme pri branju slike. Da bi naši metodi omogočili pravilno delovanje s paradigmo asinhronega programiranja, smo predstavili while zanka, ki naenkrat bere koščke slike in začasno ustavi izvajanje, medtem ko čaka na dokončanje V / I. To omogoča zanki dogodka, da se vrti skozi prenos različnih slik, saj ima vsaka med prenosom na voljo nove podatke.

Moral bi biti en - po možnosti samo en - očiten način za to

Medtem ko zen Pythona nam pove, da bi moral obstajati en očiten način, da v Pythonu obstaja veliko načinov za uvedbo sočasnosti v naše programe. Najboljši način izbire bo odvisen od vašega primera uporabe. Asinhrona paradigma se bolje prilagaja delovnim obremenitvam z visoko hkratnostjo (kot je spletni strežnik) v primerjavi z navoji ali večprocesorskimi obdelavami, vendar zahteva, da je vaša koda (in odvisnosti) asinhronizirana, da lahko v celoti izkoristi.

Upajmo, da vas bodo primeri navojev Python v tem članku - in posodobitve - usmerili v pravo smer, tako da boste imeli idejo, kje iskati v standardni knjižnici Python, če želite v svoje programe vnesti sočasnost.

Razumevanje osnov

Kaj je nit v Pythonu?

Nit je lahek postopek ali naloga. Nit je eden od načinov za dodajanje sočasnosti vašim programom. Če vaša aplikacija Python uporablja več niti in pogledate procese, ki se izvajajo v vašem OS, boste videli samo en vnos za svoj skript, čeprav ima več niti.

Kaj je večnitnost?

Večnitnost (včasih preprosto »navoj«) je, ko program ustvari več niti z izvedbenim ciklom med njimi, zato ena dolgotrajnejša naloga ne blokira vseh ostalih. To dobro deluje pri nalogah, ki jih lahko razdelimo na manjše podopravila, ki jih nato lahko damo v nit, ki jo je treba dokončati.

Kakšna je razlika med navojem Python in večprocesorsko obdelavo?

Z navojem navojev sočasno dosežemo z več nitmi, vendar se zaradi GIL-a lahko izvaja samo ena nit naenkrat. Pri večprocesorski obdelavi je prvotni postopek razdeljen na več podrejenih procesov, mimo GIL. Vsak podrejeni postopek bo imel kopijo celotnega pomnilnika programa.

Kako sta povezana večnitnost in večprocesorska obdelava Pythona?

Večnitnost in večprocesorska obdelava omogočata sočasno izvajanje kode Python. Samo večprocesorska obdelava bo omogočila, da bo vaša koda resnično vzporedna. Če pa je vaša koda IO težka (na primer zahteve HTTP), bo večnitnost še vedno verjetno pospešila kodo.

Predstavitveno oblikovanje in umetnost vizualnega pripovedovanja zgodb

Oblikovanje Blagovne Znamke

Predstavitveno oblikovanje in umetnost vizualnega pripovedovanja zgodb
H-1B: Potovanje razvijalca za iOS iz Hondurasa v Silicijevo dolino

H-1B: Potovanje razvijalca za iOS iz Hondurasa v Silicijevo dolino

Življenjski Slog

Priljubljene Objave
Zakaj odkupi delnic ne uspejo? Nekaj ​​predlaganih pravnih sredstev
Zakaj odkupi delnic ne uspejo? Nekaj ​​predlaganih pravnih sredstev
10 najpogostejših napak, ki jih naredijo razvijalci WordPressa
10 najpogostejših napak, ki jih naredijo razvijalci WordPressa
Spoznajte Ecto, brezkompromisni ovitek za zbirko podatkov za sočasne aplikacije Elixir
Spoznajte Ecto, brezkompromisni ovitek za zbirko podatkov za sočasne aplikacije Elixir
Ustvarjanje večplastnih aplikacij z Xamarin: perspektiva razvijalca za Android
Ustvarjanje večplastnih aplikacij z Xamarin: perspektiva razvijalca za Android
Kako GWT odklene razširjeno resničnost v vašem brskalniku
Kako GWT odklene razširjeno resničnost v vašem brskalniku
 
Globok potop v naložbe Elona Muska: zasluge milijarderja
Globok potop v naložbe Elona Muska: zasluge milijarderja
Kako začeti uspešen fotografski YouTube kanal
Kako začeti uspešen fotografski YouTube kanal
Kako enostavno narediti delovni prehod na daljavo
Kako enostavno narediti delovni prehod na daljavo
10 najpogostejših ranljivosti spletne varnosti
10 najpogostejših ranljivosti spletne varnosti
Oblikovalcem z ljubeznijo (pismo razvijalca)
Oblikovalcem z ljubeznijo (pismo razvijalca)
Kategorije
TehnologijaShranjevanjePostopek OblikovanjaLjudje In EkipeMobilniProces In OrodjaRise Of RemoteKpi In AnalitikaPrihodnost DelaMobilno Oblikovanje

© 2023 | Vse Pravice Pridržane

socialgekon.com