GIL là gì? Giải thích chi tiết về Python Global Interpreter Lock

Tìm hiểu GIL là gì, và cách nó tác động đến các chương trình trong Python
Please wait 0 seconds...
Scroll Down and click on Go to Link for destination
Congrats! Link is Generated
GIL là gì? Giải thích chi tiết về Python Global Interpreter Lock

Global Interpreter Lock là gì?

Global Interpreter Lock (GIL) là một mutex (khóa) được sử dụng trong CPython (triển khai Python tiêu chuẩn) để đồng bộ hóa việc thực thi các thread. GIL đảm bảo chỉ có một thread có thể thực thi mã Python tại một thời điểm, ngay cả khi chạy trên máy tính đa nhân.

Tại sao Python lại cần GIL?

Chắc hẳn nhiều bạn cũng đã thắc mắc, tại sao Python lại cần GIL? Tại sao lại không cho phép nhiều thread có thể thực thi mã đồng thời?

Vấn đề nằm ở việc cách Python quản lý bộ nhớ. Python sử dụng cơ chế reference counting để quản lý bộ nhớ:

  • Reference Counting: Môi đối tượng trong Python có một bộ đếm tham chiếu, theo dõi số lượng tham chiếu đến đối tượng đó.
  • Garbage Colletion: Khi bộ đếm về 0, bộ nhớ của đối tượng sẽ được giải phóng.

Nếu không có GIL, việc nhiều thread cùng truy cập và sửa đổi bộ nhớ đệm tham chiếu có thể dẫn đến race condition và memory leak.

Để biết race condition và memory leak là gì, bạn hãy tham khảo bài viết sau nhé

Ví dụ về tác động của GIL

CPU Bound Operations

import threading
import time

def cpu_bound(n):
    while n > 0:
        n -= 1

# Single thread
start = time.time()
cpu_bound(100000000)
print(f"Single thread time: {time.time() - start}")

# Multiple threads
start = time.time()
t1 = threading.Thread(target=cpu_bound, args=(50000000,))
t2 = threading.Thread(target=cpu_bound, args=(50000000,))
t1.start()
t2.start()
t1.join()
t2.join()
print(f"Multi thread time: {time.time() - start}")

"""
Kết quả:
Single thread time: 5.841715335845947
Multi thread time: 6.014959096908569
"""

Trong ví dụ trên, phiên bản đa luồng có thể chậm hơn do chi phí chuyển đổi context giữa các thread.

I/O Bound Operations

import threading
import time
import requests

def io_bound():
    response = requests.get('https://api.github.com')
    return response.status_code

# Single thread
start = time.time()
for _ in range(10):
    io_bound()
print(f"Single thread time: {time.time() - start}")

# Multiple threads
start = time.time()
threads = []
for _ in range(10):
    t = threading.Thread(target=io_bound)
    t.start()
    threads.append(t)
for t in threads:
    t.join()
print(f"Multi thread time: {time.time() - start}")

"""
Kết quả:
Single thread time: 2.6462595462799072
Multi thread time: 0.4881858825683594
"""

Với Operations I/O Bound, đa luồng vẫn hiệu quả vì GIL được giải phóng trong khi chờ I/O.

Giải pháp thay thế cho GIL

Multiprocessing

from multiprocessing import Process
import time

def cpu_intensive(n):
    while n > 0:
        n -= 1

if __name__ == '__main__':
    start = time.time()
    p1 = Process(target=cpu_intensive, args=(50000000,))
    p2 = Process(target=cpu_intensive, args=(50000000,))
    p1.start()
    p2.start()
    p1.join()
    p2.join()
    print(f"Multi process time: {time.time() - start}")


"""
Kết quả:
Multi process time: 2.9250338077545166
"""

Async/Await

import asyncio
import aiohttp

async def fetch_url(session, url):
    async with session.get(url) as response:
        return await response.text()

async def main():
    async with aiohttp.ClientSession() as session:
        tasks = []
        for _ in range(10):
            task = asyncio.create_task(
                fetch_url(session, 'https://api.github.com')
            )
            tasks.append(task)
        await asyncio.gather(*tasks)

asyncio.run(main())

Khi nào GIL ảnh hưởng đến hiệu suất

CPU-bound tasks

  • Tính toán số học phức tạp
  • Xử lý hình ảnh
  • Machine learning algorithms

Khi GIL ít ảnh hưởng:

  • I/O Operation (file / network)
  • Multiprocessing
  • C extensions (Có thể giải phóng GIL)

Tối ưu code với GIL

  • Sử dụng multiprocessing cho CPU-bound tasks
  • Async / await cho I/O-bound tasks

Kết luận

GIL là một phần không thể thiếu của CPython, mang lại cả ưu và nhược điểm:

  • Ưu điểm:
    • Đơn giản hóa việc quản lý bộ nhớ
    • Tăng hiệu suất cho single-threaded programs
    • Dễ dành tích hợp với C extensions
  • Nhược điểm:
    • Giới hạn tru paralleism cho CPU-bound tasks
    • Có thể gây bottleneck trong một số trường hợp

Để tối ưu hiệu suất ứng dụng Python, chúng ta cần phải:

  • Hiểu rõ tác động của GIL
  • Chọn giải pháp phù hợp (threading, multiprocessing, async/await)
  • Thiết kế kiến trúc phù hợp với đặc điểm của GIL

Đăng nhận xét

Tham gia cuộc trò chuyện