Luồng GIL Python

Khi tôi sáu tuổi, tôi có một hộp nhạc. Tôi sẽ cuộn nó lại, và một nữ diễn viên ba lê xoay tròn trên đỉnh hộp trong khi một cơ chế bên trong phát ra tiếng "Twinkle, Twinkle, Little Star. " Thứ này hẳn là dính kinh khủng, nhưng tôi yêu chiếc hộp âm nhạc đó, và tôi muốn biết nó hoạt động như thế nào. Bằng cách nào đó, tôi đã mở được nó và được tưởng thưởng khi nhìn thấy một thiết bị đơn giản—một ống trụ kim loại có kích thước bằng ngón tay cái của tôi, được nạm đinh để khi xoay, nó sẽ nhổ răng của một chiếc lược thép và tạo ra các nốt nhạc.

Trong tất cả các đặc điểm của một lập trình viên, sự tò mò về cách mọi thứ hoạt động là điều kiện thiết yếu. Khi tôi mở chiếc hộp nhạc của mình để xem bên trong, tôi đã cho thấy rằng lớn lên mình có thể trở thành, nếu không phải là một lập trình viên giỏi, thì ít nhất cũng là một người tò mò

Lập trình và phát triển

  • Blog nhà phát triển mũ đỏ
  • Lập trình cheat sheet
  • Thử miễn phí. Đăng ký học Red Hat
  • sách điện tử. Giới thiệu về lập trình với Bash
  • Bash Shell Scripting Cheat Sheet
  • sách điện tử. Hiện đại hóa doanh nghiệp Java

Sau đó, thật kỳ lạ là trong nhiều năm, tôi đã viết các chương trình Python trong khi vẫn giữ những quan niệm sai lầm về khóa trình thông dịch toàn cầu [GIL], bởi vì tôi chưa bao giờ đủ tò mò để xem nó hoạt động như thế nào. Tôi đã gặp những người khác với sự do dự tương tự, và sự thiếu hiểu biết tương tự. Đã đến lúc chúng ta cạy hộp. Hãy đọc mã nguồn của trình thông dịch CPython và tìm hiểu chính xác GIL là gì, tại sao Python lại có GIL và nó ảnh hưởng đến các chương trình đa luồng của bạn như thế nào. Tôi sẽ đưa ra các ví dụ để giúp bạn mò mẫm GIL. Bạn sẽ học cách viết Python nhanh và an toàn theo luồng cũng như cách chọn giữa các luồng và quy trình

[Để tập trung, tôi chỉ mô tả CPython ở đây—không phải Jython, PyPy hay IronPython. CPython là triển khai Python mà các lập trình viên đang làm việc sử dụng rất nhiều. ]

Kìa, khóa thông dịch viên toàn cầu

Đây rồi

static PyThread_type_lock interpreter_lock = 0; /* This is the GIL */

Dòng mã này nằm trong. Nhận xét của Guido van Rossum, "Đây là GIL," đã được thêm vào năm 2003, nhưng bản thân khóa đã có từ trình thông dịch Python đa luồng đầu tiên của ông vào năm 1997. Trên các hệ thống Unix, PyThread_type_lock là bí danh cho khóa C tiêu chuẩn, mutex_t. Nó được khởi tạo khi trình thông dịch Python bắt đầu

void
PyEval_InitThreads[void]
{
    interpreter_lock = PyThread_allocate_lock[];
    PyThread_acquire_lock[interpreter_lock];
}

Tất cả mã C trong trình thông dịch phải giữ khóa này trong khi thực thi Python. Guido lần đầu tiên xây dựng Python theo cách này vì nó đơn giản và mọi nỗ lực loại bỏ GIL khỏi CPython đều khiến các chương trình đơn luồng tốn quá nhiều hiệu năng để xứng đáng với lợi ích đa luồng

Hiệu ứng của GIL đối với các luồng trong chương trình của bạn đủ đơn giản để bạn có thể viết nguyên tắc trên mu bàn tay của mình. "Một luồng chạy Python, trong khi N luồng khác ngủ hoặc chờ I/O. " Chủ đề Python cũng có thể đợi một luồng. Khóa hoặc đối tượng đồng bộ hóa khác khỏi mô-đun phân luồng;

Khi nào chủ đề chuyển đổi? . Đây là đa nhiệm hợp tác. CPython cũng có đa nhiệm ưu tiên. Nếu một luồng chạy không bị gián đoạn đối với 1000 lệnh mã byte trong Python 2 hoặc chạy 15 mili giây trong Python 3, thì luồng đó sẽ từ bỏ GIL và một luồng khác có thể chạy. Hãy nghĩ về điều này giống như việc cắt thời gian trong những ngày xa xưa khi chúng ta có nhiều luồng nhưng một CPU. Tôi sẽ thảo luận chi tiết về hai loại đa nhiệm này

Hãy nghĩ về Python như một máy tính lớn cũ;

đa nhiệm hợp tác

Khi nó bắt đầu một tác vụ, chẳng hạn như I/O mạng, có thời lượng dài hoặc không chắc chắn và không yêu cầu chạy bất kỳ mã Python nào, một luồng sẽ từ bỏ GIL để một luồng khác có thể lấy nó và chạy Python. Hành vi lịch sự này được gọi là đa nhiệm hợp tác và nó cho phép đồng thời;

Nói rằng hai luồng mỗi kết nối một ổ cắm

def do_connect[]:
    s = socket.socket[]
    s.connect[['python.org', 80]]  # drop the GIL

for i in range[2]:
    t = threading.Thread[target=do_connect]
    t.start[]

Chỉ một trong hai luồng này có thể thực thi Python tại một thời điểm, nhưng khi luồng đã bắt đầu kết nối, nó sẽ loại bỏ GIL để luồng khác có thể chạy. Điều này có nghĩa là cả hai luồng có thể đang đợi ổ cắm của chúng kết nối đồng thời, đây là một điều tốt. Họ có thể làm nhiều việc hơn trong cùng một khoảng thời gian

Hãy mở hộp và xem cách một chuỗi Python thực sự bỏ GIL trong khi chờ kết nối được thiết lập, trong socketmodule. c

/* s.connect[[host, port]] method */
static PyObject *
sock_connect[PySocketSockObject *s, PyObject *addro]
{
    sock_addr_t addrbuf;
    int addrlen;
    int res;

    /* convert [host, port] tuple to C address */
    getsockaddrarg[s, addro, SAS2SA[&addrbuf], &addrlen];

    Py_BEGIN_ALLOW_THREADS
    res = connect[s->sock_fd, addr, addrlen];
    Py_END_ALLOW_THREADS

    /* error handling and so on ... */
}

Macro Py_BEGIN_ALLOW_THREADS là nơi chuỗi giảm GIL;

PyThread_release_lock[interpreter_lock];

Và tất nhiên Py_END_ALLOW_THREADS yêu cầu lại khóa. Một luồng có thể chặn tại điểm này, đợi một luồng khác giải phóng khóa; . Nói ngắn gọn. Trong khi N luồng bị chặn trên mạng I/O hoặc đang chờ yêu cầu lại GIL, một luồng có thể chạy Python

Dưới đây, hãy xem một ví dụ hoàn chỉnh sử dụng đa nhiệm hợp tác để tìm nạp nhiều URL một cách nhanh chóng. Nhưng trước đó, hãy so sánh đa nhiệm hợp tác với các loại đa nhiệm khác

ưu tiên đa nhiệm

Một luồng Python có thể tự nguyện giải phóng GIL, nhưng nó cũng có thể chiếm giữ GIL từ nó trước

Hãy sao lưu và nói về cách Python được thực thi. Chương trình của bạn được chạy trong hai giai đoạn. Đầu tiên, văn bản Python của bạn được biên dịch thành định dạng nhị phân đơn giản hơn được gọi là bytecode. Thứ hai, vòng lặp chính của trình thông dịch Python, một hàm có tên dễ hiểu là PyEval_EvalFrameEx[], đọc mã byte và thực hiện từng lệnh trong đó

Trong khi trình thông dịch bước qua mã byte của bạn, nó định kỳ bỏ GIL mà không cần xin phép luồng có mã mà nó đang thực thi, vì vậy các luồng khác có thể chạy

for [;;] {
    if [--ticker < 0] {
        ticker = check_interval;
    
        /* Give another thread a chance */
        PyThread_release_lock[interpreter_lock];
    
        /* Other threads may run now */
    
        PyThread_acquire_lock[interpreter_lock, 1];
    }

    bytecode = *next_instr++;
    switch [bytecode] {
        /* execute the next instruction .. */ 
    }
}

Theo mặc định, khoảng thời gian kiểm tra là 1000 mã byte. Tất cả các luồng chạy cùng một mã này và khóa được lấy từ chúng theo định kỳ theo cùng một cách. Trong Python 3, việc triển khai GIL phức tạp hơn và khoảng thời gian kiểm tra không phải là một số mã byte cố định, mà là 15 mili giây. Tuy nhiên, đối với mã của bạn, những khác biệt này không đáng kể

An toàn chủ đề trong Python

Dệt nhiều sợi lại với nhau đòi hỏi kỹ năng

Nếu một chuỗi có thể mất GIL bất cứ lúc nào, bạn phải làm cho mã của mình an toàn cho chuỗi. Tuy nhiên, các lập trình viên Python nghĩ khác về sự an toàn của luồng so với các lập trình viên C hoặc Java, bởi vì nhiều thao tác Python là nguyên tử

Một ví dụ về hoạt động nguyên tử đang gọi sort[] trên danh sách. Một luồng không thể bị gián đoạn khi đang sắp xếp và các luồng khác không bao giờ thấy danh sách được sắp xếp một phần, cũng như không thấy dữ liệu cũ từ trước khi danh sách được sắp xếp. Các hoạt động nguyên tử đơn giản hóa cuộc sống của chúng ta, nhưng có những điều bất ngờ. Ví dụ: += có vẻ đơn giản hơn so với sort[], nhưng += không phải là nguyên tử. Làm thế nào bạn có thể biết hoạt động nào là nguyên tử và hoạt động nào không?

Hãy xem xét mã này

n = 0

def foo[]:
    global n
    n += 1

Chúng ta có thể thấy mã byte mà chức năng này biên dịch, với mô-đun dis tiêu chuẩn của Python

>>> import dis
>>> dis.dis[foo]
LOAD_GLOBAL              0 [n]
LOAD_CONST               1 [1]
INPLACE_ADD
STORE_GLOBAL             0 [n]

Một dòng mã, n += 1, đã được biên dịch thành bốn mã byte, thực hiện bốn thao tác nguyên thủy

  1. tải giá trị của n vào ngăn xếp
  2. tải hằng số 1 vào ngăn xếp
  3. tổng hai giá trị ở đầu ngăn xếp
  4. lưu trữ tổng trở lại vào n

Hãy nhớ rằng cứ sau 1000 mã byte, một luồng sẽ bị gián đoạn bởi trình thông dịch lấy GIL đi. Nếu luồng không may mắn, điều này có thể xảy ra giữa thời điểm nó tải giá trị của n vào ngăn xếp và khi nó lưu nó trở lại. Làm thế nào điều này dẫn đến các bản cập nhật bị mất là dễ dàng nhìn thấy

________số 8_______

Thông thường mã này in 100, vì mỗi luồng trong số 100 luồng đã tăng n. Nhưng đôi khi bạn thấy 99 hoặc 98, nếu một trong các cập nhật của luồng bị ghi đè bởi một luồng khác

Vì vậy, mặc dù có GIL, bạn vẫn cần khóa để bảo vệ trạng thái có thể thay đổi được chia sẻ

n = 0
lock = threading.Lock[]

def foo[]:
    global n
    with lock:
        n += 1

Thay vào đó, nếu chúng ta đang sử dụng một phép toán nguyên tử như sort[] thì sao?

void
PyEval_InitThreads[void]
{
    interpreter_lock = PyThread_allocate_lock[];
    PyThread_acquire_lock[interpreter_lock];
}
0

Mã byte của hàm này cho thấy rằng sort[] không thể bị gián đoạn, bởi vì nó là nguyên tử

void
PyEval_InitThreads[void]
{
    interpreter_lock = PyThread_allocate_lock[];
    PyThread_acquire_lock[interpreter_lock];
}
1

Một dòng biên dịch thành ba mã byte

  1. tải giá trị của lst vào ngăn xếp
  2. tải phương thức sắp xếp của nó lên ngăn xếp
  3. gọi phương thức sắp xếp

Mặc dù dòng lst. sort[] mất vài bước, bản thân cuộc gọi sắp xếp là một mã byte đơn và do đó, không có cơ hội nào để luồng có GIL bị thu giữ từ nó trong cuộc gọi. Chúng ta có thể kết luận rằng chúng ta không cần khóa sort[]. Hoặc, để tránh lo lắng về hoạt động nào là nguyên tử, hãy làm theo một quy tắc đơn giản. Luôn khóa các lần đọc và ghi trạng thái có thể thay đổi được chia sẻ. Rốt cuộc, có được một luồng. Khóa bằng Python rất rẻ

Mặc dù GIL không miễn trừ cho chúng tôi nhu cầu về khóa, nhưng điều đó có nghĩa là không cần khóa chi tiết. Trong một ngôn ngữ luồng tự do như Java, các lập trình viên cố gắng khóa dữ liệu được chia sẻ trong thời gian ngắn nhất có thể, để giảm tranh chấp luồng và cho phép xử lý song song tối đa. Tuy nhiên, vì các luồng không thể chạy Python song song, nên không có lợi thế nào đối với khóa chi tiết. Miễn là không có luồng nào giữ khóa trong khi nó ở chế độ ngủ, thực hiện I/O hoặc một số hoạt động loại bỏ GIL khác, bạn nên sử dụng các khóa thô nhất, đơn giản nhất có thể. Các chủ đề khác không thể chạy song song

Kết thúc sớm hơn với đồng thời

Tôi cá rằng những gì bạn thực sự làm là để tối ưu hóa các chương trình của bạn với đa luồng. Nếu nhiệm vụ của bạn sẽ kết thúc sớm hơn bằng cách đợi nhiều hoạt động mạng cùng một lúc, thì nhiều luồng sẽ giúp ích, mặc dù chỉ một trong số chúng có thể thực thi Python tại một thời điểm. Đây là đồng thời và luồng hoạt động độc đáo trong trường hợp này

Mã này chạy nhanh hơn với chủ đề

void
PyEval_InitThreads[void]
{
    interpreter_lock = PyThread_allocate_lock[];
    PyThread_acquire_lock[interpreter_lock];
}
2

Như chúng ta đã thấy ở trên, các luồng này bỏ GIL trong khi chờ từng hoạt động của ổ cắm liên quan đến việc tìm nạp URL qua HTTP, vì vậy chúng hoàn thành công việc sớm hơn một luồng đơn lẻ có thể

song song

Điều gì sẽ xảy ra nếu nhiệm vụ của bạn sẽ hoàn thành sớm hơn chỉ bằng cách chạy đồng thời mã Python? . Bạn phải sử dụng nhiều quy trình, có thể phức tạp hơn luồng và yêu cầu nhiều bộ nhớ hơn, nhưng nó sẽ tận dụng nhiều CPU

Ví dụ này kết thúc sớm hơn bằng cách rẽ nhánh 10 quy trình so với chỉ một quy trình, bởi vì các quy trình chạy song song trên một số lõi. Nhưng nó sẽ không chạy nhanh hơn với 10 luồng so với một luồng, bởi vì chỉ một luồng có thể thực thi Python tại một thời điểm

void
PyEval_InitThreads[void]
{
    interpreter_lock = PyThread_allocate_lock[];
    PyThread_acquire_lock[interpreter_lock];
}
3

Bởi vì mỗi quy trình rẽ nhánh có một GIL riêng biệt, chương trình này có thể chia nhỏ công việc và chạy nhiều tính toán cùng một lúc

[Jython và IronPython cung cấp song song một quá trình, nhưng chúng không tương thích hoàn toàn với CPython. PyPy với Bộ nhớ giao dịch phần mềm một ngày nào đó có thể nhanh. Hãy thử những thông dịch viên này nếu bạn tò mò. ]

Phần kết luận

Bây giờ bạn đã mở hộp nhạc và nhìn thấy cơ chế đơn giản, bạn biết tất cả những gì bạn cần để viết Python nhanh, an toàn theo luồng. Sử dụng các luồng cho I/O đồng thời và các quy trình để tính toán song song. Nguyên tắc đơn giản đến mức bạn có thể không cần viết nó lên tay

A. Jesse Jiryu Davis sẽ phát biểu tại PyCon 2017, được tổ chức vào ngày 17-25 tháng 5 tại Portland, Oregon. Bắt kịp cuộc nói chuyện của anh ấy, Grok the GIL. Viết Python nhanh và an toàn cho luồng, vào thứ Sáu, ngày 19 tháng 5

Đọc gì tiếp theo

Thẻ

con trăn

lập trình

A. Jesse

Tôi là kỹ sư nhân viên tại MongoDB ở thành phố New York. Tôi đã viết Motor, trình điều khiển MongoDB Python không đồng bộ và tôi là nhà phát triển chính của Trình điều khiển MongoDB C. Tôi đóng góp cho PyMongo, asyncio, Python và Tornado. Tôi học tại Trung tâm Nhiếp ảnh Quốc tế và thực hành tại Village Zendo

Thêm về tôi

15 bình luận

Những bình luận này đã bị đóng, tuy nhiên bạn có thể Đăng ký hoặc Đăng nhập để đăng bình luận về một bài viết khác

Yakup Keskindağ. 18 Tháng Tư, 2017

Đăng ký hoặc Đăng nhập để thích

Xin chào Jesse, Đây là bài viết hay nhất tôi từng đọc về GIL và lập trình Đa luồng trong Python. Cảm ơn rất nhiều ;]

Stephen trắng. 20 Tháng Tư, 2017

Đăng ký hoặc Đăng nhập để thích

Bài viết hay, nhưng có một vài vấn đề với nó. [a] Các mô-đun mở rộng cũng có thể giải phóng GIL trong các hoạt động thuần túy của CPU. Đáng chú ý nhất, numpy thực hiện điều này nếu bạn thực hiện các thao tác trên ma trận lớn, như dấu chấm, dấu + hoặc thậm chí là sắp xếp. . Nếu bạn đang làm một việc gì đó chuyên sâu về tính toán thì dù sao bạn cũng nên sử dụng thứ gì đó như thế này, bởi vì mã lõi được viết bằng C và sẽ chạy nhanh hơn nhiều so với một vòng lặp cuộn thủ công được viết bằng Python. Đây là quan niệm sai lầm chính về GIL và thật đáng tiếc khi bài viết này tuyên truyền nó. [b] Mặc dù bạn đã đề cập rằng bạn chỉ viết về CPython, nhưng tôi không nghĩ rằng bạn đã nói rõ rằng nội dung về các hoạt động riêng lẻ là nguyên tử [như sắp xếp] chỉ áp dụng cho CPython. Ngoài ra, một mã op trong phiên bản Python ngày nay có thể là nhiều mã trong phiên bản mới hơn. Vì vậy, cuộc thảo luận của bạn bằng cách sử dụng dis rất thú vị về mặt học thuật nhưng bạn thực sự nên khóa thủ công trong những trường hợp như vậy

Nate Guerin. 21 Tháng Tư, 2017

Đăng ký hoặc Đăng nhập để thích

Bài viết hay và rất hữu ích về cách bạn bao gồm mã CPython cho nền

Tuy nhiên, tôi nghĩ rằng tuyên bố của bạn về danh sách. sort[] là nguyên tử không chính xác. Trong trường hợp có thể ưu tiên với nhiều luồng, không có gì ngăn cản luồng 1 bắt đầu sắp xếp danh sách tại chỗ, hết 1000 mã byte hoặc 15 mili giây được phân bổ cho nó, sau đó luồng khác trở thành luồng hiện đang chạy và nối thêm một . Đây sẽ là một vấn đề. Một cơ chế khóa bên ngoài là cần thiết để đảm bảo danh sách. hàm sort[] không bị gián đoạn

Từ tài liệu

"Chi tiết triển khai CPython. Trong khi danh sách đang được sắp xếp, tác động của việc cố gắng thay đổi hoặc thậm chí kiểm tra, danh sách không được xác định. Việc triển khai C của Python làm cho danh sách xuất hiện trống trong một khoảng thời gian và tăng ValueError nếu nó có thể phát hiện ra rằng danh sách đã bị thay đổi trong quá trình sắp xếp. "

A. Jesse. 21 Tháng Tư, 2017

Đăng ký hoặc Đăng nhập để thích

cảm ơn. Kiểm tra mã byte. danh sách. sort[] là một mã byte duy nhất, vì vậy nó không thể bị gián đoạn. Tài liệu bạn tham khảo mô tả cách tiện ích mở rộng C phải tương tác với danh sách, trong khi nó đang được sắp xếp, nếu tiện ích mở rộng C đang chạy một chuỗi không giữ GIL. Mã Python của bạn luôn giữ GIL trong khi nó chạy, và do đó nó không bao giờ có thể nhìn thấy danh sách *trong khi* nó đang được sắp xếp theo một luồng khác

Trả lời bởi Nate Guerin

Nate Guerin. 21 Tháng Tư, 2017

Đăng ký hoặc Đăng nhập để thích

Cảm ơn đã làm rõ. Điều này có ý nghĩa miễn là GIL sẽ không bị từ bỏ trong mã CPython, mà từ các ví dụ khác của bạn dường như chỉ được thực hiện một cách có ý thức bởi mã phụ thuộc vào IO, mà tôi lấy từ những gì bạn đã viết là trường hợp

Trả lời bởi A. Jesse

Ben Darnell. 30 Tháng Tư, 2017

Đăng ký hoặc Đăng nhập để thích

`danh sách. sort[]` không bị gián đoạn nếu nó chứa số. Nhưng nếu nó chứa các đối tượng python tùy ý, có thể có các phương thức `__cmp__` tùy ý của riêng chúng, thì sắp xếp *có thể* bị gián đoạn

Tương tự, các đối tượng có các phương thức `__hash__` tùy chỉnh có thể làm cho các hoạt động mã byte đơn trở nên gián đoạn theo những cách đôi khi gây ngạc nhiên. Tháo gỡ không phải là một chỉ số đáng tin cậy về những thứ được GIL tạo ra nguyên tử;

Trả lời bởi A. Jesse

A. Jesse. 1 Tháng Năm, 2017

Đăng ký hoặc Đăng nhập để thích

Cảm ơn Ben, điều đó hoàn toàn chính xác. Tôi đã nghĩ về điều đó gần đây và tôi đã cập nhật văn bản trên trang cá nhân của mình

Trả lời bởi Ben Darnell [chưa được xác minh]

Wladimir Mutel. 30 Tháng Tư, 2017

Đăng ký hoặc Đăng nhập để thích

Là. pop[] có an toàn cho luồng không?
i. e. , nó có thể được áp dụng một cách an toàn cho danh sách [url] được chia sẻ không?

A. Jesse. 1 Tháng Năm, 2017

Đăng ký hoặc Đăng nhập để thích

Có, với những lưu ý tương tự như Ben Darnell đã chỉ ra trong các nhận xét tại đây

Trả lời bởi Wladimir Mutel [chưa được xác minh]

Louie Lữ. 17 Tháng Năm, 2017

Đăng ký hoặc Đăng nhập để thích

Xin chào Jesse, cảm ơn vì bài viết tuyệt vời của bạn. Mặc dù trang web dán nhãn CC-BY-SA, tôi muốn hỏi bạn, tôi có thể dịch bài viết của bạn sang tiếng Trung Quốc phồn thể và chia sẻ trên blog của tôi không? . //Blog. louie. lu], cảm ơn

Ngoài ra, bạn đang sử dụng 2. 7 làm ví dụ, bạn có định sử dụng 3. x chẳng hạn trong tương lai?

A. Jesse. 17 Tháng Năm, 2017

Đăng ký hoặc Đăng nhập để thích

Xin chào Louie, vâng, vui lòng dịch nó

tôi chọn 2. 7 vì việc triển khai GIL của nó đơn giản hơn và dễ hiểu hơn Python 3. Tôi không có kế hoạch thay đổi ví dụ

Trả lời bởi Louie Lu [chưa được xác minh]

Josh. 9 Tháng Bảy, 2017

Đăng ký hoặc Đăng nhập để thích

trong phần khóa bạn đưa ra tuyên bố. "Miễn là không có luồng nào giữ khóa trong khi nó ở chế độ ngủ, thực hiện I/O hoặc một số hoạt động loại bỏ GIL khác, bạn nên sử dụng các khóa thô nhất, đơn giản nhất có thể. Các chủ đề khác không thể chạy song song. "

Điều gì ngăn việc đánh rơi GIL trước xảy ra khi bạn có khóa?

A. Jesse. 9 Tháng Bảy, 2017

Đăng ký hoặc Đăng nhập để thích

Xin chào. Không có gì ngăn chặn một chủ đề bỏ GIL ưu tiên trong khi nó giữ khóa. Hãy gọi Chủ đề đó là A và giả sử cũng có Chủ đề B. Nếu Chủ đề A giữ khóa và được ưu tiên, thì có thể Chủ đề B có thể chạy thay vì Chủ đề A

Nếu Chủ đề B đang đợi khóa mà Chủ đề A đang giữ, thì Chủ đề B *không* đang chờ GIL. Trong trường hợp đó, Chủ đề A yêu cầu lại GIL ngay sau khi bỏ nó và Chủ đề A tiếp tục

Nếu Chủ đề B không đợi khóa mà Chủ đề A đang giữ, thì Chủ đề B có thể lấy GIL và chạy

Tuy nhiên, quan điểm của tôi về ổ khóa thô là đây. không có hai luồng nào có thể thực thi Python song song, vì GIL. Vì vậy, sử dụng khóa chi tiết không cải thiện thông lượng. Điều này trái ngược với một ngôn ngữ như Java hoặc C, trong đó các khóa chi tiết cho phép xử lý song song lớn hơn và do đó thông lượng lớn hơn

Trả lời bởi Josh

Josh. 9 Tháng Bảy, 2017

Đăng ký hoặc Đăng nhập để thích

Cảm ơn cho phản ứng nhanh chóng của bạn

Nếu tôi hiểu bạn một cách chính xác, thì mục đích của câu lệnh mà tôi đã tham chiếu là để tránh sử dụng các khóa xung quanh các hoạt động bên ngoài, khi đó bạn có thể chặn nhiều luồng, nếu tất cả chúng đều phụ thuộc vào khóa đó

Đối với ví dụ ưu tiên, Chủ đề A không bị chặn bởi bất kỳ thứ gì bên ngoài, vì vậy quá trình xử lý chỉ diễn ra qua lại tương tự như đa nhiệm hợp tác

GIL có tạo chuỗi Python không

Mã an toàn luồng . Chẳng hạn, với GIL tại chỗ, việc tích hợp tiện ích mở rộng C không an toàn theo luồng sẽ dễ dàng hơn vì bạn có thể lấy và giải phóng GIL từ mã C một cách rõ ràng, do đó làm cho tiện ích mở rộng của bạn an toàn theo luồng ở cấp độ Python. The GIL's protection occurs at the interpreter-state level. With the GIL in place, for instance, the integration of non-thread-safe C extension is easier because you can explicitly acquire and release the GIL from the C code, thus making your extension thread-safe at the Python level.

Luồng có tốt trong Python không?

Phân luồng Python cho phép bạn chạy đồng thời các phần khác nhau của chương trình và có thể đơn giản hóa thiết kế của bạn . Nếu bạn đã có một số kinh nghiệm về Python và muốn tăng tốc chương trình của mình bằng các luồng, thì hướng dẫn này là dành cho bạn.

Nhóm luồng Python là gì?

Nhóm luồng có thể được định nghĩa là nhóm các luồng không hoạt động và được khởi tạo trước, sẵn sàng hoạt động . Tạo nhóm luồng được ưu tiên hơn so với khởi tạo luồng mới cho mọi tác vụ khi chúng ta cần thực hiện số lượng lớn tác vụ.

Chủ Đề