Hur du förbättrar din Python-kod med samtidighet och parallellism

Nyckel takeaways

  • Samtidighet och parallellitet är grundläggande principer för uppgiftsutförande i datoranvändning, där var och en har sina distinkta egenskaper.
  • Samtidighet möjliggör ett effektivt resursutnyttjande och förbättrad lyhördhet för applikationer, medan parallellitet är avgörande för optimal prestanda och skalbarhet.
  • Python tillhandahåller alternativ för hantering av samtidighet, såsom gängning och asynkron programmering med asyncio, samt parallellitet med multiprocessormodulen.

Samtidighet och parallellism är två tekniker som låter dig köra flera program samtidigt. Python har flera alternativ för att hantera uppgifter samtidigt och parallellt, vilket kan vara förvirrande.

Utforska verktygen och biblioteken som är tillgängliga för att korrekt implementera samtidighet och parallellism i Python, och hur de skiljer sig åt.

Förstå samtidighet och parallellism

Samtidighet och parallellitet hänvisar till två grundläggande principer för uppgiftsutförande i datoranvändning. Var och en har sina distinkta egenskaper.

  • Samtidighet är förmågan hos ett program att hantera flera uppgifter samtidigt utan att nödvändigtvis utföra dem på exakt samma tidpunkt. Det kretsar kring idén att interfoliera uppgifter, växla mellan dem på ett sätt som verkar samtidigt.
  • Parallellism, å andra sidan, innebär att utföra flera uppgifter genuint parallellt. Det drar vanligtvis fördel av flera CPU-kärnor eller processorer. Parallellism uppnår sann simultan exekvering, låter dig utföra uppgifter snabbare och är väl lämpad för beräkningsintensiva operationer.
  • Vikten av samtidighet och parallellism

    Behovet av samtidighet och parallellitet i beräkningar kan inte överskattas. Här är varför dessa tekniker är viktiga:

  • Resursutnyttjande: Samtidighet möjliggör ett effektivt utnyttjande av systemresurser, vilket säkerställer att uppgifter aktivt gör framsteg snarare än att vänta på externa resurser.
  • Lyhördhet: Samtidighet kan förbättra applikationernas lyhördhet, särskilt i scenarier som involverar användargränssnitt eller webbservrar.
  • Prestanda: Parallellism är avgörande för att uppnå optimal prestanda, särskilt i CPU-bundna uppgifter som komplexa beräkningar, databehandling och simuleringar.
  • Skalbarhet: Både samtidighet och parallellitet är avgörande för att bygga skalbara system.
  • Framtidssäkring: När hårdvaratrender fortsätter att gynna flerkärniga processorer, kommer möjligheten att utnyttja parallellitet att bli alltmer nödvändig.
  • Samtidighet i Python

    Du kan uppnå samtidighet i Python genom att använda trådning och asynkron programmering med asyncio-biblioteket.

    Träning i Python

    Threading är en Python-samtidsmekanism som låter dig skapa och hantera uppgifter inom en enda process. Trådar är lämpliga för vissa typer av uppgifter, särskilt de som är I/O-bundna och kan dra nytta av samtidig körning.

    Pythons gängningsmodul ger ett gränssnitt på hög nivå för att skapa och hantera trådar. Medan GIL (Global Interpreter Lock) begränsar trådar i termer av sann parallellitet, kan de fortfarande uppnå samtidighet genom att interfoliera uppgifter effektivt.

    Koden nedan visar ett exempel på implementering av samtidighet med hjälp av trådar. Den använder Python-förfrågningsbiblioteket för att skicka en HTTP-förfrågan, en vanlig I/O-blockeringsuppgift. Den använder också tidsmodulen för att beräkna exekveringstiden.

     import requests
    import time
    import threading

    urls = [
        'https://www.google.com',
        'https://www.wikipedia.org',
        'https://www.makeuseof.com',
    ]


    def download_url(url):
        response = requests.get(url)
        print(f"Downloaded {url} - Status Code: {response.status_code}")


    start_time = time.time()

    for url in urls:
        download_url(url)

    end_time = time.time()
    print(f"Sequential download took {end_time - start_time:.2f} seconds\n")


    start_time = time.time()
    threads = []

    for url in urls:
        thread = threading.Thread(target=download_url, args=(url,))
        thread.start()
        threads.append(thread)


    for thread in threads:
        thread.join()

    end_time = time.time()
    print(f"Threaded download took {end_time - start_time:.2f} seconds")

    När du kör det här programmet bör du se hur mycket snabbare de trådade förfrågningarna är än de sekventiella förfrågningarna. Även om skillnaden bara är en bråkdel av en sekund får du en tydlig känsla av prestandaförbättringen när du använder trådar för I/O-bundna uppgifter.

    Asynkron programmering med Asyncio

    asyncio tillhandahåller en händelseloop som hanterar asynkrona uppgifter som kallas koroutiner. Coroutiner är funktioner som du kan pausa och återuppta, vilket gör dem idealiska för I/O-bundna uppgifter. Biblioteket är särskilt användbart för scenarier där uppgifter involverar att vänta på externa resurser, till exempel nätverksbegäranden.

    Du kan ändra det tidigare exemplet för att skicka begäran så att det fungerar med asyncio:

     import asyncio
    import aiohttp
    import time

    urls = [
        'https://www.google.com',
        'https://www.wikipedia.org',
        'https://www.makeuseof.com',
    ]


    async def download_url(url):
        async with aiohttp.ClientSession() as session:
            async with session.get(url) as response:
                content = await response.text()
                print(f"Downloaded {url} - Status Code: {response.status}")


    async def main():
        
        tasks = [download_url(url) for url in urls]

        
        await asyncio.gather(*tasks)

    start_time = time.time()


    asyncio.run(main())

    end_time = time.time()

    print(f"Asyncio download took {end_time - start_time:.2f} seconds")

    Med hjälp av koden kan du ladda ner webbsidor samtidigt med asyncio och dra fördel av asynkrona I/O-operationer. Detta kan vara mer effektivt än trådning för I/O-bundna uppgifter.

    Parallellism i Python

    Du kan implementera parallellism med hjälp av Pythons multiprocessormodulvilket gör att du kan dra full nytta av flerkärniga processorer.

    Multiprocessing i Python

    Pythons multiprocessormodul ger ett sätt att uppnå parallellitet genom att skapa separata processer, var och en med sin egen Python-tolkare och minnesutrymme. Detta förbigår effektivt Global Interpreter Lock (GIL), vilket gör det lämpligt för CPU-bundna uppgifter.

     import requests
    import multiprocessing
    import time

    urls = [
        'https://www.google.com',
        'https://www.wikipedia.org',
        'https://www.makeuseof.com',
    ]


    def download_url(url):
        response = requests.get(url)
        print(f"Downloaded {url} - Status Code: {response.status_code}")

    def main():
        
        num_processes = len(urls)
        pool = multiprocessing.Pool(processes=num_processes)

        start_time = time.time()
        pool.map(download_url, urls)
        end_time = time.time()

        
        pool.close()
        pool.join()

        print(f"Multiprocessing download took {end_time-start_time:.2f} seconds")

    main()

    I det här exemplet skapar multiprocessing flera processer, vilket gör att funktionen download_url kan köras parallellt.

    När ska man använda samtidighet eller parallellism

    Valet mellan samtidighet och parallellitet beror på typen av dina uppgifter och tillgängliga hårdvaruresurser.

    Du kan använda samtidighet när du hanterar I/O-bundna uppgifter, som att läsa och skriva till filer eller göra nätverksbegäranden, och när minnesbegränsningar är ett problem.

    Använd multiprocessing när du har CPU-bundna uppgifter som kan dra nytta av sann parallellitet och när du har robust isolering mellan uppgifter, där en uppgifts misslyckande inte bör påverka andra.

    Dra fördel av samtidighet och parallellism

    Parallellism och samtidighet är effektiva sätt att förbättra responsen och prestandan hos din Python-kod. Det är viktigt att förstå skillnaderna mellan dessa koncept och välja den mest effektiva strategin.

    Python erbjuder de verktyg och moduler du behöver för att göra din kod mer effektiv genom samtidighet eller parallellitet, oavsett om du arbetar med CPU-bundna eller I/O-bundna processer.