Hướng dẫn python multithreading append to list - python đa luồng nối thêm vào danh sách

Bản tóm tắt

Tại sao đầu ra là lộn xộn?

==> Bởi vì một luồng có thể mang lại một phần thông qua việc thực thi câu lệnh print

Tại sao aList không bằng [1, 2, 3, 4, 5, 6]?

==> Bởi vì nội dung của aList có thể thay đổi giữa việc đọc từ nó và nối vào nó.

Đầu ra

Đầu ra là lộn xộn vì nó đang được tạo ra bởi câu lệnh print của Python2 từ bên trong các luồng và câu lệnh print không phải là sleadSafe. Điều này có nghĩa là một luồng có thể mang lại trong khi print đang thực thi. Trong mã trong câu hỏi có nhiều luồng in, do đó, một luồng có thể mang lại trong khi in, luồng khác có thể bắt đầu in và sau đó mang lại để tạo ra đầu ra xen kẽ được OP nhìn thấy. Các hoạt động của IO như ghi vào stdout rất chậm trong các thuật ngữ CPU, do đó, rất có khả năng hệ điều hành có thể tạm dừng một luồng thực hiện IO vì chủ đề đang chờ phần cứng để làm gì đó.

Ví dụ: mã này:

import threading


def printer():
    for i in range(2):
        print ['foo', 'bar', 'baz']


def main():
    threads = [threading.Thread(target=printer) for x in xrange(2)]
    for t in threads: 
        t.start()
    for t in threads:
        t.join()

Sản xuất đầu ra xen kẽ này:

>>> main()
['foo', 'bar'['foo', , 'bar', 'baz']
'baz']
['foo', ['foo', 'bar''bar', 'baz']
, 'baz']

Hành vi xen kẽ có thể được ngăn chặn bằng cách sử dụng

>>> main()
['foo', 'bar'['foo', , 'bar', 'baz']
'baz']
['foo', ['foo', 'bar''bar', 'baz']
, 'baz']
0:

def printer():
    for i in range(2):
        with lock:
            print ['foo', 'bar', 'baz']


def main():
    global lock
    lock = threading.Lock()
    threads = [threading.Thread(target=printer) for x in xrange(2)]
    for t in threads: 
        t.start()
    for t in threads:
        t.join()

>>> main()
['foo', 'bar', 'baz']
['foo', 'bar', 'baz']
['foo', 'bar', 'baz']
['foo', 'bar', 'baz']

Nội dung của danh sách

Nội dung cuối cùng của aList sẽ là

>>> main()
['foo', 'bar'['foo', , 'bar', 'baz']
'baz']
['foo', ['foo', 'bar''bar', 'baz']
, 'baz']
2 nếu câu lệnh

>>> main()
['foo', 'bar'['foo', , 'bar', 'baz']
'baz']
['foo', ['foo', 'bar''bar', 'baz']
, 'baz']
3

được thực thi về mặt nguyên tử, đó là không có luồng hiện tại mang lại cho một luồng khác cũng đang đọc và nối lại đến aList.

Tuy nhiên đây không phải là cách chủ đề hoạt động. Một luồng có thể mang lại sau khi đọc phần tử cuối cùng từ aList hoặc tăng giá trị, vì vậy hoàn toàn có thể có một chuỗi sự kiện như thế này:

  1. Chủ đề1 đọc giá trị
    >>> main()
    ['foo', 'bar'['foo', , 'bar', 'baz']
    'baz']
    ['foo', ['foo', 'bar''bar', 'baz']
    , 'baz']
    
    6 từ aList
  2. Chủ đề1 mang lại
  3. Thread2 đọc giá trị
    >>> main()
    ['foo', 'bar'['foo', , 'bar', 'baz']
    'baz']
    ['foo', ['foo', 'bar''bar', 'baz']
    , 'baz']
    
    6 từ aList, sau đó nối lại
    def printer():
        for i in range(2):
            with lock:
                print ['foo', 'bar', 'baz']
    
    
    def main():
        global lock
        lock = threading.Lock()
        threads = [threading.Thread(target=printer) for x in xrange(2)]
        for t in threads: 
            t.start()
        for t in threads:
            t.join()
    
    >>> main()
    ['foo', 'bar', 'baz']
    ['foo', 'bar', 'baz']
    ['foo', 'bar', 'baz']
    ['foo', 'bar', 'baz']
    
    0
  4. Thread2 đọc giá trị
    def printer():
        for i in range(2):
            with lock:
                print ['foo', 'bar', 'baz']
    
    
    def main():
        global lock
        lock = threading.Lock()
        threads = [threading.Thread(target=printer) for x in xrange(2)]
        for t in threads: 
            t.start()
        for t in threads:
            t.join()
    
    >>> main()
    ['foo', 'bar', 'baz']
    ['foo', 'bar', 'baz']
    ['foo', 'bar', 'baz']
    ['foo', 'bar', 'baz']
    
    0 từ aList, sau đó nối lại
    def printer():
        for i in range(2):
            with lock:
                print ['foo', 'bar', 'baz']
    
    
    def main():
        global lock
        lock = threading.Lock()
        threads = [threading.Thread(target=printer) for x in xrange(2)]
        for t in threads: 
            t.start()
        for t in threads:
            t.join()
    
    >>> main()
    ['foo', 'bar', 'baz']
    ['foo', 'bar', 'baz']
    ['foo', 'bar', 'baz']
    ['foo', 'bar', 'baz']
    
    3
  5. Chủ đề2 năng suất
  6. Thread1 Bổ sung
    def printer():
        for i in range(2):
            with lock:
                print ['foo', 'bar', 'baz']
    
    
    def main():
        global lock
        lock = threading.Lock()
        threads = [threading.Thread(target=printer) for x in xrange(2)]
        for t in threads: 
            t.start()
        for t in threads:
            t.join()
    
    >>> main()
    ['foo', 'bar', 'baz']
    ['foo', 'bar', 'baz']
    ['foo', 'bar', 'baz']
    ['foo', 'bar', 'baz']
    
    0
  7. Thread1 đọc giá trị
    def printer():
        for i in range(2):
            with lock:
                print ['foo', 'bar', 'baz']
    
    
    def main():
        global lock
        lock = threading.Lock()
        threads = [threading.Thread(target=printer) for x in xrange(2)]
        for t in threads: 
            t.start()
        for t in threads:
            t.join()
    
    >>> main()
    ['foo', 'bar', 'baz']
    ['foo', 'bar', 'baz']
    ['foo', 'bar', 'baz']
    ['foo', 'bar', 'baz']
    
    0 từ aList, sau đó nối lại
    def printer():
        for i in range(2):
            with lock:
                print ['foo', 'bar', 'baz']
    
    
    def main():
        global lock
        lock = threading.Lock()
        threads = [threading.Thread(target=printer) for x in xrange(2)]
        for t in threads: 
            t.start()
        for t in threads:
            t.join()
    
    >>> main()
    ['foo', 'bar', 'baz']
    ['foo', 'bar', 'baz']
    ['foo', 'bar', 'baz']
    ['foo', 'bar', 'baz']
    
    3

Điều này để lại aList dưới dạng

def printer():
    for i in range(2):
        with lock:
            print ['foo', 'bar', 'baz']


def main():
    global lock
    lock = threading.Lock()
    threads = [threading.Thread(target=printer) for x in xrange(2)]
    for t in threads: 
        t.start()
    for t in threads:
        t.join()

>>> main()
['foo', 'bar', 'baz']
['foo', 'bar', 'baz']
['foo', 'bar', 'baz']
['foo', 'bar', 'baz']
9

Như với các câu lệnh print, điều này có thể được ngăn chặn bằng cách làm cho các luồng có được

>>> main()
['foo', 'bar'['foo', , 'bar', 'baz']
'baz']
['foo', ['foo', 'bar''bar', 'baz']
, 'baz']
0 trước khi thực hiện
>>> main()
['foo', 'bar'['foo', , 'bar', 'baz']
'baz']
['foo', ['foo', 'bar''bar', 'baz']
, 'baz']
3

.

Takeaways thread-safe list by using a mutual exclusion (mutex) lock via the threading.Lock class.

Bây giờ bạn đã biết cách sử dụng một danh sách an toàn chủ đề trong Python.

Bạn có câu hỏi nào không? Đặt câu hỏi của bạn trong các ý kiến ​​dưới đây và tôi sẽ cố gắng hết sức để trả lời.

  • Ảnh của Harley-Davidson trên unplash
  • Nhiều chủ đề có thể nối vào một danh sách trong Python không?
  • Chúng ta có thể nối một cách an toàn vào một tệp từ nhiều luồng bằng khóa loại trừ lẫn nhau. Python cung cấp một khóa loại trừ lẫn nhau, còn được gọi là mutex, thông qua luồng. LỚP LỚN. Đầu tiên, chúng ta có thể tạo một thể hiện của khóa để được chia sẻ bởi tất cả các luồng.
  • Đang nối vào một luồng danh sách
  • Mặc dù danh sách trong đa luồng là không an toàn, nhưng nó an toàn theo luồng theo hoạt động của phụ lục.
  • Là chủ đề danh sách
  • Ví dụ về việc sử dụng hàng đợi an toàn chủ đề
  • # Bắt đầu chủ đề
  • 'Main Chờ đợi trước ...

Ảnh của Harley-Davidson trên unplash

Nhiều chủ đề có thể nối vào một danh sách trong Python không?

Chúng ta có thể nối một cách an toàn vào một tệp từ nhiều luồng bằng khóa loại trừ lẫn nhau. Python cung cấp một khóa loại trừ lẫn nhau, còn được gọi là mutex, thông qua luồng. LỚP LỚN. Đầu tiên, chúng ta có thể tạo một thể hiện của khóa để được chia sẻ bởi tất cả các luồng.

Đang nối vào một luồng danh sách

Mặc dù danh sách trong đa luồng là không an toàn, nhưng nó an toàn theo luồng theo hoạt động của phụ lục.threading.Thread class.

Là chủ đề danh sách

  • Chủ đề trong Python: Hướng dẫn hoàn chỉnh

Trong lập trình đồng thời, chúng ta có thể cần chia sẻ cấu trúc dữ liệu danh sách giữa các luồng.

Nhiều luồng có thể cần nối dữ liệu vào cùng một danh sách, các luồng khác có thể muốn xóa các mục hoặc kiểm tra độ dài của danh sách.

Là danh sách an toàn trong Python và nếu không, làm thế nào chúng ta có thể làm cho nó an toàn?

Hầu hết các hoạt động danh sách là nguyên tử

Nhiều hoạt động phổ biến trong danh sách là nguyên tử, có nghĩa là chúng an toàn cho luồng.

Nhớ lại, một danh sách là một ví dụ của lớp danh sách.List class.

Nguyên tử có nghĩa là hoạt động xảy ra hoặc không xảy ra không có ở giữa trạng thái không nhất quán.

Các hoạt động như thêm, xóa và đọc một giá trị trong danh sách là nguyên tử.

Trong thực tế, điều đó có nghĩa là các hoạt động trên các biến được chia sẻ của các loại dữ liệu tích hợp (ints, danh sách, dicts, v.v.) mà nhìn ra nguyên tử.

-Những loại đột biến giá trị toàn cầu là an toàn cho luồng?

Specifically:

  • Nối thêm một giá trị thông qua append ().append().
  • Thêm một danh sách vào một danh sách thông qua mở rộng ().extend().
  • Gán nhiều giá trị của một danh sách thông qua một lát.
  • Nhận một giá trị từ một danh sách thông qua ký hiệu khung [].[].
  • Xóa giá trị cuối cùng khỏi danh sách thông qua pop ().pop().
  • Sắp xếp một danh sách thông qua sắp xếp ().sort().

Bạn có thể tìm hiểu về các hoạt động nguyên tử trong Python tại đây:

  • Hoạt động nguyên tử chủ đề trong Python

Bởi vì các hoạt động nguyên tử xảy ra hoặc không xảy ra, điều đó có nghĩa là chúng an toàn cho luồng.

Cụ thể, sử dụng bất kỳ hoạt động nào ở trên trong danh sách được chia sẻ giữa nhiều luồng sẽ không dẫn đến điều kiện chủng tộc, tham nhũng của danh sách hoặc tham nhũng dữ liệu trong danh sách.

Tiếp theo, hãy để Lừa xem xét lý do tại sao dựa vào các hoạt động danh sách nguyên tử có thể mong manh.

Bị nhầm lẫn bởi API mô -đun luồng? Tải xuống bảng cheat pdf miễn phí của tôi
Download my FREE PDF cheat sheet

Hoạt động danh sách nguyên tử rất dễ vỡ

Các hoạt động phổ biến trong danh sách là nguyên tử và do đó chủ đề an toàn như chúng ta đã thấy trong phần trước.

Điều này chỉ đúng tại thời điểm viết vì một vài cân nhắc cụ thể, chẳng hạn như:

  • Các chi tiết chính xác về cách các hoạt động danh sách này được chuyển đổi thành mã byte của máy ảo Python.
  • Việc sử dụng trình thông dịch Python tham chiếu.
  • Việc sử dụng khóa phiên dịch toàn cầu (GIL) trong trình thông dịch Python tham chiếu.

Điều này có nghĩa là tùy thuộc vào an toàn chủ đề của các hoạt động này có thể mong manh trong các phiên bản Python trong tương lai hoặc khi thực hiện chương trình Python của bạn với các phiên dịch viên Python thay thế.

Đây không phải là một mối quan tâm nhỏ.

Có những nỗ lực phát triển thường xuyên để cải thiện thông dịch viên Python và thậm chí cố gắng loại bỏ Gil. Những điều này có thể sẽ thay đổi các chi tiết cụ thể của VM Python, biên dịch mã byte và an toàn chủ đề của các cấu trúc dữ liệu tích hợp.

Nó cũng đang trở nên phổ biến hơn để chạy mã Python bằng cách sử dụng các phiên dịch viên của bên thứ ba, chủ yếu để đạt được hiệu suất tốt hơn. Thông dịch viên thay thế có thể hoặc không thể thực hiện các quy tắc tương tự cho các hoạt động nguyên tử trong danh sách.

Do đó, chúng tôi có thể mong muốn một danh sách Python an toàn chủ đề là chống lại các thay đổi đối với các phiên dịch viên Python và GIL.

Khi nghi ngờ, hãy sử dụng một mutex!

-Những loại đột biến giá trị toàn cầu là an toàn cho luồng?

Nối thêm một giá trị thông qua append ().


Thêm một danh sách vào một danh sách thông qua mở rộng ().

Gán nhiều giá trị của một danh sách thông qua một lát.

Nhận một giá trị từ một danh sách thông qua ký hiệu khung [].

Xóa giá trị cuối cùng khỏi danh sách thông qua pop ().
 


Sắp xếp một danh sách thông qua sắp xếp ().

Bạn có thể tìm hiểu về các hoạt động nguyên tử trong Python tại đây:

Hoạt động nguyên tử chủ đề trong Python

Bởi vì các hoạt động nguyên tử xảy ra hoặc không xảy ra, điều đó có nghĩa là chúng an toàn cho luồng.

Cụ thể, sử dụng bất kỳ hoạt động nào ở trên trong danh sách được chia sẻ giữa nhiều luồng sẽ không dẫn đến điều kiện chủng tộc, tham nhũng của danh sách hoặc tham nhũng dữ liệu trong danh sách.threading.Lock can be used to protect the list.

Tiếp theo, hãy để Lừa xem xét lý do tại sao dựa vào các hoạt động danh sách nguyên tử có thể mong manh.

  • Bị nhầm lẫn bởi API mô -đun luồng? Tải xuống bảng cheat pdf miễn phí của tôi

Hoạt động danh sách nguyên tử rất dễ vỡ

Các hoạt động phổ biến trong danh sách là nguyên tử và do đó chủ đề an toàn như chúng ta đã thấy trong phần trước.

Điều này chỉ đúng tại thời điểm viết vì một vài cân nhắc cụ thể, chẳng hạn như:..

Các chi tiết chính xác về cách các hoạt động danh sách này được chuyển đổi thành mã byte của máy ảo Python.

lock.acquire().acquire()

Việc sử dụng trình thông dịch Python tham chiếu.

Điều này chỉ đúng tại thời điểm viết vì một vài cân nhắc cụ thể, chẳng hạn như:..

Các chi tiết chính xác về cách các hoạt động danh sách này được chuyển đổi thành mã byte của máy ảo Python.

lock.release().release()

Việc sử dụng trình thông dịch Python tham chiếu.

Việc sử dụng khóa phiên dịch toàn cầu (GIL) trong trình thông dịch Python tham chiếu.

Các hoạt động phổ biến trong danh sách là nguyên tử và do đó chủ đề an toàn như chúng ta đã thấy trong phần trước.

Điều này chỉ đúng tại thời điểm viết vì một vài cân nhắc cụ thể, chẳng hạn như:..

Các chi tiết chính xác về cách các hoạt động danh sách này được chuyển đổi thành mã byte của máy ảo Python.

Việc sử dụng trình thông dịch Python tham chiếu.lock:

Việc sử dụng trình thông dịch Python tham chiếu.

Điều này chỉ đúng tại thời điểm viết vì một vài cân nhắc cụ thể, chẳng hạn như:..

Các chi tiết chính xác về cách các hoạt động danh sách này được chuyển đổi thành mã byte của máy ảo Python.

Việc sử dụng trình thông dịch Python tham chiếu.

Điều này có thể đạt được thông qua lớp Threading.Rlock. Nó sẽ cho phép khóa được thu được bởi cùng một luồng một lần nữa.threading.RLock class. It will allow the lock to be acquired by the same thread again.

Bạn có thể tìm hiểu thêm về khóa reentrant ở đây:

  • Cách sử dụng khóa Reentrant trong Python

Hãy để phát triển một lớp kết thúc một số hoạt động danh sách phổ biến theo cách an toàn cho luồng.

Một cách tiếp cận sẽ là ghi đè lớp Danh sách và triển khai các phiên bản an toàn luồng của tất cả hoặc một tập hợp con của các chức năng.List class and implement thread-safe versions of all or a subset of functions.

Một cách tiếp cận khác là xác định một lớp mới có cùng giao diện với lớp Danh sách, ngoại trừ các chức năng trên lớp là an toàn cho luồng.List class, except the functions on the class are threads-safe.

Cách tiếp cận cuối cùng là xác định một lớp mới với các hoạt động giống như danh sách, có thể không giống với giao diện của lớp Danh sách.List class.

Tôi thích cách tiếp cận thứ hai để làm rõ cho người dùng của lớp rằng họ đang làm việc với một cái gì đó khác biệt. Nó không phải là một sự thay thế khe cắm cho danh sách an toàn, nó là một cấu trúc dữ liệu được thiết kế cho một trường hợp sử dụng an toàn luồng phải được xử lý một cách cực kỳ chăm sóc.List that is thread safe, it is a data structure designed for a thread-safe use case that must be treated with extreme care.

Đầu tiên, chúng ta có thể xác định lớp với một tên rõ ràng như ThreadSafelist.ThreadSafeList.

# lớp tùy chỉnh gói danh sách để làm cho nó an toàn

classThreadSafeList():ThreadSafeList():

# ...

Tiếp theo, chúng ta có thể xác định một hàm tạo khởi tạo cả danh sách nội bộ và khóa được sử dụng để bảo vệ danh sách dưới dạng các biến thể hiện trên lớp.

# người xây dựng

def __init __ (tự):__init__(self):

& nbsp; & nbsp; & nbsp; & nbsp;# khởi tạo danh sách# initialize the list

    self._list=list()self._list=list()

& nbsp; & nbsp; & nbsp; & nbsp;# khởi tạo khóa# initialize the lock

    self._lock=Lock()self._lock =Lock()

Tiếp theo, chúng ta có thể bắt đầu thêm chức năng mà chúng ta có thể cần trong ứng dụng của mình.

Trong trường hợp này, chúng tôi sẽ cung cấp bốn hoạt động trong danh sách:

  • Thêm một giá trị thông qua append ().append().
  • Xóa và trả về một giá trị thông qua pop ().pop().
  • Truy cập một giá trị tại một chỉ mục thông qua get ().get().
  • Nhận độ dài của danh sách thông qua độ dài ().length().

Mỗi thao tác trong danh sách sẽ được khóa bởi khóa bằng giao diện Trình quản lý ngữ cảnh.

Giao diện trình quản lý bối cảnh được ưa thích như một hoạt động trong danh sách có thể gây ra lỗi (ví dụ: chỉ mục ra khỏi giới hạn) và chúng tôi cần đảm bảo rằng khóa sẽ luôn được phát hành, bất kể thành công hay thất bại của hoạt động.

Ví dụ: phương pháp cho hoạt động phụ lục được liệt kê dưới đây.

# Thêm giá trị vào danh sách

def expend (tự, giá trị):append(self,value):

& nbsp; & nbsp; & nbsp; & nbsp;# có được khóa# acquire the lock

& nbsp; & nbsp; & nbsp; & nbsp; với self._lock:with self._lock:

& nbsp; & nbsp; & nbsp; & nbsp; & nbsp; & nbsp; & nbsp; & nbsp;# append the value

        self._list.append(value)self._list.append(value)

Các phương pháp cho các hoạt động POP, GET và LENGTH được liệt kê dưới đây.

1

2

3

4

5

6

7

8

9

10

11

12

13

14

15

16

17

18

19

# Xóa và trả về giá trị cuối cùng khỏi danh sách

def pop (tự):pop(self):

& nbsp; & nbsp; & nbsp; & nbsp;# có được khóa# acquire the lock

& nbsp; & nbsp; & nbsp; & nbsp; với self._lock:with self._lock:

& nbsp; & nbsp; & nbsp; & nbsp; & nbsp; & nbsp; & nbsp; & nbsp;# pop a value from the list

        returnself._list.pop()return self._list.pop()

Các phương pháp cho các hoạt động POP, GET và LENGTH được liệt kê dưới đây.

# Xóa và trả về giá trị cuối cùng khỏi danh sáchget(self,index):

& nbsp; & nbsp; & nbsp; & nbsp;# có được khóa# acquire the lock

& nbsp; & nbsp; & nbsp; & nbsp; với self._lock:with self._lock:

& nbsp; & nbsp; & nbsp; & nbsp; & nbsp; & nbsp; & nbsp; & nbsp;# read a value at the index

        returnself._list[index]returnself._list[index]

Các phương pháp cho các hoạt động POP, GET và LENGTH được liệt kê dưới đây.

# Xóa và trả về giá trị cuối cùng khỏi danh sáchlength(self):

& nbsp; & nbsp; & nbsp; & nbsp;# có được khóa# acquire the lock

& nbsp; & nbsp; & nbsp; & nbsp; với self._lock:with self._lock:

        returnlen(self._list)returnlen(self._list)

& nbsp; & nbsp; & nbsp; & nbsp; & nbsp; & nbsp; & nbsp; & nbsp;

Các phương pháp cho các hoạt động POP, GET và LENGTH được liệt kê dưới đây.

# Xóa và trả về giá trị cuối cùng khỏi danh sách

def pop (tự):

1

2

3

4

5

6

7

8

9

10

11

12

13

14

15

16

17

18

19

20

21

22

23

24

25

26

27

28

29

30

31

32

33

34

35

# lớp tùy chỉnh gói danh sách để làm cho nó an toàn

classThreadSafeList():ThreadSafeList():

# ...# constructor

Tiếp theo, chúng ta có thể xác định một hàm tạo khởi tạo cả danh sách nội bộ và khóa được sử dụng để bảo vệ danh sách dưới dạng các biến thể hiện trên lớp.def __init__(self):

# người xây dựng# initialize the list

        self._list=list()self._list=list()

def __init __ (tự):# initialize the lock

        self._lock=Lock()self._lock= Lock()

& nbsp; & nbsp; & nbsp; & nbsp;# khởi tạo danh sách# add a value to the list

& nbsp; & nbsp; & nbsp; & nbsp;# khởi tạo khóadef append(self,value):

Tiếp theo, chúng ta có thể bắt đầu thêm chức năng mà chúng ta có thể cần trong ứng dụng của mình.# acquire the lock

Trong trường hợp này, chúng tôi sẽ cung cấp bốn hoạt động trong danh sách:with self._lock:

Thêm một giá trị thông qua append ().# append the value

            self._list.append(value)self._list.append(value)

Xóa và trả về một giá trị thông qua pop ().# remove and return the last value from the list

& nbsp; & nbsp; & nbsp; & nbsp; def pop (self):def pop(self):

& nbsp; & nbsp; & nbsp; & nbsp; & nbsp; & nbsp; & nbsp; & nbsp;# acquire the lock

& nbsp; & nbsp; & nbsp; & nbsp; & nbsp; & nbsp;with self._lock:

& nbsp; & nbsp; & nbsp; & nbsp; & nbsp; & nbsp; & nbsp; & nbsp; & nbsp;# pop a value from the list

            returnself._list.pop()returnself._list.pop()

& nbsp; & nbsp; & nbsp; & nbsp;# đọc một giá trị từ danh sách tại một chỉ mục# read a value from the list at an index

& nbsp; & nbsp; & nbsp; & nbsp; def get (self, index):def get(self, index):

& nbsp; & nbsp; & nbsp; & nbsp; & nbsp; & nbsp; & nbsp; & nbsp;# acquire the lock

& nbsp; & nbsp; & nbsp; & nbsp; & nbsp; & nbsp;with self._lock:

& nbsp; & nbsp; & nbsp; & nbsp; & nbsp; & nbsp; & nbsp; & nbsp; & nbsp;# read a value at the index

            returnself._list[index]return self._list[index]

& nbsp; & nbsp; & nbsp; & nbsp;# đọc một giá trị từ danh sách tại một chỉ mục# return the number of items in the list

& nbsp; & nbsp; & nbsp; & nbsp; def get (self, index):def length(self):

& nbsp; & nbsp; & nbsp; & nbsp; & nbsp; & nbsp; & nbsp; & nbsp;# acquire the lock

& nbsp; & nbsp; & nbsp; & nbsp; & nbsp; & nbsp;with self._lock:

            returnlen(self._list)returnlen(self._list)

& nbsp; & nbsp; & nbsp; & nbsp; & nbsp; & nbsp; & nbsp; & nbsp; & nbsp;sort() and extend().

& nbsp; & nbsp; & nbsp; & nbsp;# đọc một giá trị từ danh sách tại một chỉ mụcindex() or reverse().

& nbsp; & nbsp; & nbsp; & nbsp; def get (self, index):

& nbsp; & nbsp; & nbsp; & nbsp; & nbsp; & nbsp; & nbsp; & nbsp; & nbsp;

& nbsp; & nbsp; & nbsp; & nbsp;# Trả về số lượng mục trong danh sách

& nbsp; & nbsp; & nbsp; & nbsp; độ dài def (self):

Là một tiện ích mở rộng, hãy thử thêm một số hoạt động bổ sung mà bạn muốn sử dụng trong danh sách của mình như Sort () và Extend ().

Ngoài ra, hãy cố gắng thêm một số hoạt động liên quan đến các hoạt động tổng hợp trong danh sách, như index () hoặc larter ().

Tiếp theo, hãy để Lừa trình diễn an toàn chủ đề của danh sách trong một ví dụ đã làm việc.ThreadSafeList, then loop 100,000 times, adding a value to the list each iteration.

Ví dụ về danh sách an toàn chủ đềadd_items() function below implements this.

Chúng ta có thể phát triển một ví dụ về việc sử dụng danh sách an toàn luồng từ nhiều luồng.

Trong ví dụ này, chúng tôi sẽ tạo mười luồng và mỗi chủ đề cố gắng thêm 100.000 mục vào danh sách đồng thời. Kết quả dự kiến ​​sẽ là một danh sách với 1.000.000 mặt hàng.add_items(safe_list):

    foriinrange(100000):foriinrange(100000):

        safe_list.append(i)safe_list.append(i)

Một lần nữa, điều này có thể đạt được với danh sách Python mặc định bằng phiên bản hiện tại (v3.10) của trình thông dịch Python tham chiếu, nhưng nó có thể hoặc không phải là bằng chứng trong tương lai cho các thay đổi trong trình thông dịch. Do đó, chúng tôi sẽ sử dụng lớp danh sách an toàn chủ đề của chúng tôi được phát triển trong phần trước.

Đầu tiên, chúng ta có thể xác định một chức năng được thực thi bởi mỗi luồng công nhân...

Hàm sẽ có một thể hiện của ThreadSafelist, sau đó lặp lại 100.000 lần, thêm một giá trị vào danh sách mỗi lần lặp.

safe_list=ThreadSafeList()=ThreadSafeList()

Hàm add_items () bên dưới thực hiện điều này.add_items() function and be passed the same ThreadSafeList as an argument.

# Thêm các mục vào danh sách

Đầu tiên, chúng ta có thể xác định một chức năng được thực thi bởi mỗi luồng công nhân...

Hàm sẽ có một thể hiện của ThreadSafelist, sau đó lặp lại 100.000 lần, thêm một giá trị vào danh sách mỗi lần lặp.

threads=[Thread(target=add_items,args=(safe_list,))foriinrange(10)]=[Thread(target=add_items,args=(safe_list,))fori inrange(10)]

Hàm add_items () bên dưới thực hiện điều này.

Đầu tiên, chúng ta có thể xác định một chức năng được thực thi bởi mỗi luồng công nhân...

Hàm sẽ có một thể hiện của ThreadSafelist, sau đó lặp lại 100.000 lần, thêm một giá trị vào danh sách mỗi lần lặp.

Hàm add_items () bên dưới thực hiện điều này.thread inthreads:

    thread.start()thread.start()

# Thêm các mục vào danh sách

Đầu tiên, chúng ta có thể xác định một chức năng được thực thi bởi mỗi luồng công nhân...

Hàm sẽ có một thể hiện của ThreadSafelist, sau đó lặp lại 100.000 lần, thêm một giá trị vào danh sách mỗi lần lặp.

Hàm add_items () bên dưới thực hiện điều này.('Main waiting for threads...')

Hàm add_items () bên dưới thực hiện điều này.thread inthreads:

    thread.join()thread.join()

# Thêm các mục vào danh sách

Đầu tiên, chúng ta có thể xác định một chức năng được thực thi bởi mỗi luồng công nhân...

Hàm sẽ có một thể hiện của ThreadSafelist, sau đó lặp lại 100.000 lần, thêm một giá trị vào danh sách mỗi lần lặp.

Hàm add_items () bên dưới thực hiện điều này.(f'List size: {safe_list.length()}')

# Thêm các mục vào danh sách

1

2

3

4

5

6

7

8

9

10

11

12

13

14

15

16

17

18

19

20

21

22

23

24

25

26

27

28

29

30

31

32

33

34

35

36

37

38

39

40

41

42

43

44

45

46

47

48

49

50

51

52

53

54

55

56

57

58

59

def add_items (an toàn_list):

Tiếp theo, trong luồng chính, chúng ta có thể xác định một thể hiện của danh sách an toàn luồng của chúng tôi.

...threading import Thread

# Tạo danh sách an toàn chủ đềthreading import Lock

Sau đó, chúng ta có thể tạo và định cấu hình mười luồng, mỗi luồng sẽ gọi hàm add_items () của chúng ta và được truyền cùng một luồngFelist như một đối số.

classThreadSafeList():ThreadSafeList():

Điều này có thể được thực hiện trong một danh sách hiểu, để đưa ra một danh sách các chủ đề chúng ta có thể sử dụng sau này.# constructor

# Định cấu hình các luồng để thêm vào danh sáchdef __init__(self):

Danh sách các chủ đề sau đó có thể được lặp lại và bắt đầu.# initialize the list

        self._list=list()self._list=list()

# Bắt đầu chủ đề# initialize the lock

        self._lock=Lock()self._lock=Lock()

Inthreads forthreads:# add a value to the list

Chủ đề chính sau đó có thể chờ tất cả các luồng mới chấm dứt, bằng cách lặp lại danh sách các luồng và tham gia lần lượt. Điều này sẽ chặn chủ đề chính cho đến khi tất cả các tác vụ được hoàn thành.def append(self,value):

& nbsp; & nbsp; & nbsp; & nbsp; & nbsp; & nbsp; & nbsp; & nbsp;# acquire the lock

& nbsp; & nbsp; & nbsp; & nbsp; & nbsp; & nbsp;with self._lock:

# Đợi tất cả các luồng chấm dứt# append the value

            self._list.append(value)self._list.append(value)

& nbsp; & nbsp; & nbsp; & nbsp;# xóa và trả về giá trị cuối cùng từ danh sách# remove and return the last value from the list

& nbsp; & nbsp; & nbsp; & nbsp; def pop (self):def pop(self):

& nbsp; & nbsp; & nbsp; & nbsp; & nbsp; & nbsp; & nbsp; & nbsp;# acquire the lock

& nbsp; & nbsp; & nbsp; & nbsp; & nbsp; & nbsp;with self._lock:

& nbsp; & nbsp; & nbsp; & nbsp; & nbsp; & nbsp; & nbsp; & nbsp; & nbsp;# pop a value from the list

            returnself._list.pop()returnself._list.pop()

& nbsp; & nbsp; & nbsp; & nbsp;# đọc một giá trị từ danh sách tại một chỉ mục# read a value from the list at an index

& nbsp; & nbsp; & nbsp; & nbsp; def get (self, index):def get(self, index):

& nbsp; & nbsp; & nbsp; & nbsp; & nbsp; & nbsp; & nbsp; & nbsp;# acquire the lock

& nbsp; & nbsp; & nbsp; & nbsp; & nbsp; & nbsp;with self._lock:

& nbsp; & nbsp; & nbsp; & nbsp; & nbsp; & nbsp; & nbsp; & nbsp; & nbsp;# read a value at the index

            returnself._list[index]return self._list[index]

& nbsp; & nbsp; & nbsp; & nbsp;# đọc một giá trị từ danh sách tại một chỉ mục# return the number of items in the list

& nbsp; & nbsp; & nbsp; & nbsp; def get (self, index):def length(self):

& nbsp; & nbsp; & nbsp; & nbsp; & nbsp; & nbsp; & nbsp; & nbsp;# acquire the lock

& nbsp; & nbsp; & nbsp; & nbsp; & nbsp; & nbsp;with self._lock:

            returnlen(self._list)returnlen(self._list)

& nbsp; & nbsp; & nbsp; & nbsp; & nbsp; & nbsp; & nbsp; & nbsp; & nbsp;

& nbsp; & nbsp; & nbsp; & nbsp;# đọc một giá trị từ danh sách tại một chỉ mụcadd_items(safe_list):

    foriinrange(100000):for iinrange(100000):

        safe_list.append(i)safe_list.append(i)

& nbsp; & nbsp; & nbsp; & nbsp; def get (self, index):

safe_list=ThreadSafeList()=ThreadSafeList()

& nbsp; & nbsp; & nbsp; & nbsp; & nbsp; & nbsp; & nbsp; & nbsp; & nbsp;

threads=[Thread(target=add_items,args=(safe_list,))foriinrange(10)]=[Thread(target=add_items,args=(safe_list,))foriin range(10)]

& nbsp; & nbsp; & nbsp; & nbsp;# Trả về số lượng mục trong danh sách

& nbsp; & nbsp; & nbsp; & nbsp; độ dài def (self):thread inthreads:

    thread.start()thread.start()

# Thêm các mục vào danh sách

def add_items (an toàn_list):('Main waiting for threads...')

& nbsp; & nbsp; & nbsp; & nbsp; độ dài def (self):thread inthreads:

    thread.join()thread.join()

# Thêm các mục vào danh sách

def add_items (an toàn_list):(f'List size: {safe_list.length()}')

# Tạo danh sách an toàn chủ đề

# Định cấu hình các luồng để thêm vào danh sách

# Bắt đầu chủ đề

Inthreads forthreads:ThreadSafeList class might be to add a function so that a thread can get the lock once and add many items to the list in batch.

# Đợi tất cả các luồng chấm dứt

in ('Chờ các chủ đề chính ...')

# Báo cáo số lượng mục trong danh sách

in (kích thước f'list: {an toàn_list.length ()} ')

Chạy ví dụ đầu tiên tạo ra một thể hiện của danh sách an toàn luồng của chúng tôi, nội bộ xác định một thể hiện danh sách mới và khóa để bảo vệ danh sách.

Sau đó, chúng tôi định cấu hình và bắt đầu mười luồng, mỗi chủ đề rất cố gắng thêm 100.000 mục vào danh sách.

Mỗi cuộc gọi để nối () trên danh sách an toàn luồng được bảo vệ bởi khóa, đảm bảo rằng chỉ có một luồng có thể thêm một mục vào danh sách tại một thời điểm.

Một cải tiến hữu ích cho lớp ThreadSafelist có thể là thêm một hàm để một luồng có thể lấy khóa một lần và thêm nhiều mục vào danh sách theo đợt.

Các luồng chính chặn cho đến khi tất cả mười luồng mới chấm dứt.

Cuối cùng, tất cả các chủ đề kết thúc thêm các mục của họ, các luồng chính bỏ chặn và báo cáo độ dài của danh sách.

Trong trường hợp này, chúng ta có thể thấy rằng nó chứa 1.000.000 mặt hàng dự kiến. Đây sẽ là trường hợp mỗi khi mã được chạy, bất kể thay đổi trong tương lai đối với trình thông dịch tham chiếu hoặc trình thông dịch cụ thể mà mã được thực thi.queue module, such as the queue.Queue and queue.SimpleQueue classes.

Chính chờ đợi các chủ đề ...queue.Queue class are designed specifically to be thread-safe in a concurrent programming environment.

Kích thước danh sách: 1000000queue.Queue class.

Tiếp theo, hãy để Lôi nhìn vào một cách tiếp cận thay thế để phát triển một danh sách an toàn chủ đề...

Thay thế cho một danh sách an toàn chủ đề

queue=queue.Queue()=queue.Queue()

Phát triển một lớp danh sách an toàn chủ đề mới có rủi ro.put() function.

Bạn càng viết nhiều mã, bạn càng có nhiều khả năng giới thiệu các lỗi, đặc biệt là mã liên quan đến đồng thời.

Tiếp theo, hãy để Lôi nhìn vào một cách tiếp cận thay thế để phát triển một danh sách an toàn chủ đề...

Thay thế cho một danh sách an toàn chủ đề

queue.put(item).put(item)

Phát triển một lớp danh sách an toàn chủ đề mới có rủi ro.get() function.

Bạn càng viết nhiều mã, bạn càng có nhiều khả năng giới thiệu các lỗi, đặc biệt là mã liên quan đến đồng thời.

Tiếp theo, hãy để Lôi nhìn vào một cách tiếp cận thay thế để phát triển một danh sách an toàn chủ đề...

Thay thế cho một danh sách an toàn chủ đề

item=queue.get()=queue.get()

Phát triển một lớp danh sách an toàn chủ đề mới có rủi ro.

Bạn càng viết nhiều mã, bạn càng có nhiều khả năng giới thiệu các lỗi, đặc biệt là mã liên quan đến đồng thời.queue module and classes.

Một giải pháp thay thế để phát triển một danh sách an toàn chủ đề là sử dụng cấu trúc dữ liệu hàng đợi.

Python cung cấp các cấu trúc dữ liệu hàng đợi an toàn chủ đề trong mô-đun hàng đợi, chẳng hạn như các lớp hàng đợi.queue và hàng đợi.simplequeue.put() does not use an internal mutex lock.

Các lớp này, chẳng hạn như lớp hàng đợi.queue được thiết kế đặc biệt để an toàn cho luồng trong môi trường lập trình đồng thời.qsize() function and checking if the queue is full via full() or empty via empty().

Ngoài ra, hàng đợi cung cấp một số khả năng chờ đợi/thông báo thông qua các luồng nội bộ. Đối tượng điều kiện, cho phép các chủ đề chờ đợi các mục từ hàng đợi cho đến khi có các mục mới hoặc chờ đặt các mục vào hàng đợi cho đến khi nó không đầy. Những điều kiện này cũng chia sẻ cùng một mutex.threading.Condition objects, allowing threads to wait on getting items from the queue until there are new items or to wait on putting items in the queue until it is not full. These conditions also share the same mutex.

Các hoạt động đầu tiên có được một điều kiện sử dụng cùng một khóa mutex bên trong cũng được bảo vệ. Một ví dụ là nhận các mục từ hàng đợi thông qua get ().get().

Tôi khuyên bạn nên sử dụng một lớp hàng đợi thay vì phát triển lớp danh sách an toàn luồng, nếu nó cung cấp tất cả hoặc hầu hết các chức năng bạn yêu cầu trong ứng dụng của mình.

Ví dụ về việc sử dụng hàng đợi an toàn chủ đề

Chúng tôi có thể cập nhật ví dụ danh sách an toàn chủ đề của mình để sử dụng hàng đợi.queue thay vì lớp ThreadSafelist của chúng tôi.queue.Queue instead of our ThreadSafeList class.

Đầu tiên, chúng tôi phải cập nhật hàm add_items () của chúng tôi để gọi put () để thêm các mục trên hàng đợi.add_items() function to call put() to add items on the queue.

Phiên bản cập nhật của chức năng được liệt kê dưới đây.

# Thêm các mục vào danh sách

def add_items (an toàn_list):add_items(safe_list):

    foriinrange(100000):foriinrange(100000):

        safe_list.put(i)safe_list.put(i)

Sau đó, chúng ta có thể xác định biến an toàn của chúng ta là một ví dụ của hàng đợi.queue.queue.Queue.

.....

# Tạo danh sách an toàn chủ đề (Hàng đợi)

safe_list=Queue()=Queue()

Cuối cùng, khi kiểm tra kích thước của cấu trúc ở cuối chương trình, chúng ta có thể gọi hàm qsize () trên hàng đợi.qsize() function on the queue.

.....

# Tạo danh sách an toàn chủ đề (Hàng đợi)

Cuối cùng, khi kiểm tra kích thước của cấu trúc ở cuối chương trình, chúng ta có thể gọi hàm qsize () trên hàng đợi.(f'List size: {safe_list.qsize()}')

# Báo cáo số lượng mục trong danh sách

1

2

3

4

5

6

7

8

9

10

11

12

13

14

15

16

17

18

19

20

21

22

23

in (kích thước f'list: {an toàn_list.qsize ()} ')

Tying này lại với nhau, ví dụ hoàn chỉnh được liệt kê dưới đây.

# SuperfastPython.comthreading import Thread

# Ví dụ về danh sách an toàn chủ đề thông qua hàng đợiqueue import Queue

# Thêm các mục vào danh sách

def add_items (an toàn_list):add_items(safe_list):

    foriinrange(100000):for iinrange(100000):

        safe_list.put(i)safe_list.put(i)

# Tạo danh sách an toàn chủ đề (Hàng đợi)

safe_list=Queue()=Queue()

Cuối cùng, khi kiểm tra kích thước của cấu trúc ở cuối chương trình, chúng ta có thể gọi hàm qsize () trên hàng đợi.

threads=[Thread(target=add_items,args=(safe_list,))foriinrange(10)]=[Thread(target=add_items,args=(safe_list,))foriin range(10)]

# Báo cáo số lượng mục trong danh sách

in (kích thước f'list: {an toàn_list.qsize ()} ')thread inthreads:

    thread.start()thread.start()

Tying này lại với nhau, ví dụ hoàn chỉnh được liệt kê dưới đây.

# SuperfastPython.com('Main waiting for threads...')

in (kích thước f'list: {an toàn_list.qsize ()} ')thread inthreads:

    thread.join()thread.join()

# Tạo danh sách an toàn chủ đề (Hàng đợi)

Cuối cùng, khi kiểm tra kích thước của cấu trúc ở cuối chương trình, chúng ta có thể gọi hàm qsize () trên hàng đợi.(f'List size: {safe_list.qsize()}')

# Báo cáo số lượng mục trong danh sách

in (kích thước f'list: {an toàn_list.qsize ()} ')

Tying này lại với nhau, ví dụ hoàn chỉnh được liệt kê dưới đây.

# SuperfastPython.com

# Ví dụ về danh sách an toàn chủ đề thông qua hàng đợi

từ luồng nhập luồngqueue.Queue does not block when adding items – which is the majority of the work performed by the threads in this example.

từ hàng đợi hàng đợiMain waiting forthreads...

# Định cấu hình các luồng để thêm vào danh sáchsize:1000000

# Bắt đầu chủ đề

Inthreads forthreads:

# Đợi tất cả các luồng chấm dứt

  • in ('Chờ các chủ đề chính ...')

Chạy ví dụ đầu tiên tạo ra một thể hiện của hàng đợi.

  • Mười chủ đề sau đó được cấu hình và bắt đầu. Mỗi chủ đề thêm 100.000 mục vào hàng đợi đồng thời nhanh nhất có thể.

Các luồng chính chặn cho đến khi các luồng công nhân chấm dứt.

  • Cuối cùng, các luồng chính bỏ chặn và báo cáo kích thước của hàng đợi.
  • Trong trường hợp này, kích thước được báo cáo là 1.000.000 mặt hàng, như mong đợi.
  • Hơn nữa, bạn sẽ nhận thấy rằng ví dụ này nhanh hơn đáng kể để thực thi vì hàng đợi.queue không chặn khi thêm các mục - phần lớn công việc được thực hiện bởi các luồng trong ví dụ này.my book!). 

'Main Chờ đợi trước ...

Kích thước danh sách: 1000000

Đọc thêm
Ask your questions in the comments below and I will do my best to answer.

Phần này cung cấp các tài nguyên bổ sung mà bạn có thể thấy hữu ích.

Nhiều chủ đề có thể nối vào một danh sách trong Python không?

Chúng ta có thể nối một cách an toàn vào một tệp từ nhiều luồng bằng khóa loại trừ lẫn nhau.Python cung cấp một khóa loại trừ lẫn nhau, còn được gọi là mutex, thông qua luồng.LỚP LỚN.Đầu tiên, chúng ta có thể tạo một thể hiện của khóa để được chia sẻ bởi tất cả các luồng.. Python provides a mutual exclusion lock, also called a mutex, via the threading. Lock class. First, we can create an instance of the lock to be shared by all threads.

Đang nối vào một luồng danh sách

Mặc dù danh sách trong đa luồng là không an toàn, nhưng nó an toàn theo luồng theo hoạt động của phụ lục.it is thread-safe under the operation of append .

Là chủ đề danh sách

Tạo một danh sách trống.Nó thực hiện giao diện danh sách.Nó là một biến thể an toàn chủ đề của ArrayList.T đại diện cho chung chung.It is a thread-safe variant of ArrayList. T represents generic.

Là chủ đề python dict

Hoạt động trên Dict là an toàn cho luồng.Gil ngăn chặn nhiều hơn một trạng thái cập nhật chủ đề trong trình thông dịch Python.. The GIL prevents more than one thread updating state in the Python interpreter.