greenlet là một thư viện hỗ trợ lập trình vi luồng (microthread) thủ công, cho phép chuyển đổi bối cảnh giữa các hàm một cách tường minh. Trong khi đó, gevent xây dựng trên nền tảng greenlet nhưng tự động hoán đổi luồng khi gặp thao tác I/O — nhờ cơ chế "monkey patching" và vòng lặp sự kiện tích hợp.
Ví dụ với greenlet: Điều khiển luồng bằng tay
Greenlet không tự động hoán đổi — người lập trình phải chủ động gọi .switch() để chuyển sang greenlet khác.
Ví dụ 1: Chuyển đổi hai chiều giữa hai vi luồng
from greenlet import greenlet
def task_a(x, y):
result = worker_b.switch(x * 2)
print(f"task_a nhận giá trị: {result}")
def task_b(value):
print(f"task_b xử lý: {value}")
worker_a.switch(value + 10)
worker_a = greenlet(task_a)
worker_b = greenlet(task_b)
worker_a.switch(5, 3) # Bắt đầu từ worker_a
Kết quả xuất ra:
task_b xử lý: 10
task_a nhận giá trị: 20
Giải thích: worker_a khởi chạy task_a(5, 3), tính x * 2 = 10, rồi chuyển sang task_b. task_b in giá trị và trả lại 20 qua worker_a.switch(...).
Ví dụ 2: Luồng tương tác tuần tự có trạng thái
from greenlet import greenlet
def fetch_data(name):
print(f"[{name}] bắt đầu lấy dữ liệu...")
parser.switch(name.upper())
print(f"[{name}] tiếp tục xử lý sau phân tích")
def parse_input(label):
print(f"[PARSER] Nhận nhãn: {label}")
fetch_data.switch()
fetch_data = greenlet(fetch_data)
parser = greenlet(parse_input)
fetch_data.switch("user_123")
Ví dụ với gevent: Tự động hoán đổi dựa trên I/O
gevent sử dụng mô hình "cooperative multitasking": mọi hàm được chạy trong môi trường gevent sẽ tạm dừng khi gặp I/O (hoặc gọi gevent.sleep()), nhường CPU cho các tác vụ khác.
Ví dụ 1: Đồng thời thực thi hai hàm với độ trễ giả lập
import gevent
def countdown(label, n):
for i in range(n, 0, -1):
print(f"{label}: {i}")
gevent.sleep(0.3)
jobs = [
gevent.spawn(countdown, "Tác vụ A", 4),
gevent.spawn(countdown, "Tác vụ B", 3)
]
gevent.joinall(jobs)
Kết quả (không tuần tự, xen kẽ):
Tác vụ A: 4
Tác vụ B: 3
Tác vụ A: 3
Tác vụ B: 2
Tác vụ A: 2
Tác vụ B: 1
Tác vụ A: 1
Ví dụ 2: Áp dụng monkey patch để "chuyển hóa" thư viện chuẩn
from gevent import monkey
monkey.patch_all() # Ghi đè time.sleep(), socket, ssl, v.v.
import gevent
import time
def download_file(filename):
print(f"Đang tải {filename}...")
time.sleep(1.5) # Giờ đây sẽ kích hoạt hoán đổi luồng
print(f"Xong {filename}")
gevent.joinall([
gevent.spawn(download_file, "report.pdf"),
gevent.spawn(download_file, "config.json"),
gevent.spawn(download_file, "log.tar.gz")
])
Ví dụ 3: Gửi nhiều yêu cầu HTTP song song
from gevent import monkey
monkey.patch_all()
import gevent
import requests
def fetch_url(target):
try:
resp = requests.get(target, timeout=5)
print(f"{target} → {resp.status_code} ({len(resp.content)} bytes)")
except Exception as e:
print(f"Lỗi khi truy cập {target}: {e}")
urls = [
"https://httpbin.org/delay/1",
"https://httpbin.org/delay/2",
"https://httpbin.org/status/200"
]
gevent.joinall([gevent.spawn(fetch_url, u) for u in urls])
Ví dụ 4: Giải quyết tên miền song song bằng socket của gevent
from gevent import monkey
monkey.patch_socket() # Thay thế socket hệ thống
import gevent
from gevent import socket
def resolve_host(domain):
try:
ip = socket.gethostbyname(domain)
return f"{domain} → {ip}"
except socket.gaierror:
return f"{domain} → lỗi DNS"
domains = ["google.com", "github.com", "python.org"]
jobs = [gevent.spawn(resolve_host, d) for d in domains]
gevent.joinall(jobs)
for job in jobs:
print(job.value)
Ví dụ 5: Dùng hàng đợi gevent kết hợp hoán đổi chủ động
import gevent
from gevent.queue import Queue
shared_queue = Queue(maxsize=2)
def producer():
for idx in range(6):
shared_queue.put(f"item_{idx}")
print(f"Đưa vào: item_{idx}")
gevent.sleep(0.1) # Nhường quyền ngay sau mỗi lần đưa
def consumer():
while not shared_queue.empty():
item = shared_queue.get()
print(f"Tiêu thụ: {item}")
gevent.sleep(0.15)
gevent.joinall([
gevent.spawn(producer),
gevent.spawn(consumer)
])