Đối tượng mô-đun luồng trong python

Chúng ta hãy xem những gì mô-đun luồng cung cấp. Ngoài khả năng sinh ra các luồng, mô-đun luồng cũng cung cấp cấu trúc dữ liệu đồng bộ hóa cơ bản được gọi là đối tượng khóa (a. k. a. khóa nguyên thủy, khóa đơn giản, khóa loại trừ lẫn nhau, mutex và semaphore nhị phân). Như chúng tôi đã đề cập trước đó, các nguyên tắc đồng bộ hóa như vậy đi đôi với quản lý luồng

Bảng 4-1 liệt kê các hàm luồng được sử dụng phổ biến hơn và các phương thức đối tượng khóa LockType

Bảng 4-1. mô-đun luồng và đối tượng khóa

Chức năng/Phương pháp

Sự miêu tả

chức năng mô-đun chủ đề

start_new_thread(hàm, args, kwargs=Không)

Sinh ra một luồng mới và thực thi chức năng với các đối số đã cho và kwargs tùy chọn

phân bổ_lock()

Cấp phát đối tượng khóa LockType

lối ra()

Hướng dẫn một chủ đề để thoát

Các phương thức đối tượng khóa LockType

có được (chờ = Không)

Nỗ lực để có được đối tượng khóa

bị khóa()

Trả về True nếu có được khóa, Sai nếu không

giải phóng()

phát hành khóa

Chức năng chính của mô-đun luồng là start_new_thread(). Nó nhận một hàm (đối tượng) cộng với các đối số và tùy chọn, các đối số từ khóa. Một chủ đề mới được sinh ra đặc biệt để gọi chức năng

Hãy lấy onethr của chúng ta. ví dụ py và tích hợp luồng vào nó. Bằng cách thay đổi một chút lời gọi hàm loop*(), giờ đây chúng ta trình bày mtsleepA. py trong Ví dụ 4-2

Ví dụ 4-2. Sử dụng Mô-đun luồng (mtsleepA. py)

Các vòng lặp tương tự từ onethr. py được thực thi, nhưng lần này sử dụng cơ chế đa luồng đơn giản do mô-đun luồng cung cấp. Hai vòng lặp được thực thi đồng thời (rõ ràng là vòng lặp ngắn hơn kết thúc trước) và tổng thời gian đã trôi qua chỉ dài bằng luồng chậm nhất chứ không phải tổng thời gian cho từng vòng riêng biệt

1    #!/usr/bin/env python
2
3    import thread
4    from time import sleep, ctime
5
6    def loop0():
7        print 'start loop 0 at:', ctime()
8        sleep(4)
9        print 'loop 0 done at:', ctime()
10
11   def loop1():
12       print 'start loop 1 at:', ctime()
13       sleep(2)
14       print 'loop 1 done at:', ctime()
15
16   def main():
17       print 'starting at:', ctime()
18       thread.start_new_thread(loop0, ())
19       thread.start_new_thread(loop1, ())
20       sleep(6)
21       print 'all DONE at:', ctime()
22
23   if __name__ == '__main__':
24       main()

start_new_thread() yêu cầu hai đối số đầu tiên, vì vậy đó là lý do để chuyển vào một bộ trống ngay cả khi chức năng thực thi không yêu cầu đối số

Khi thực hiện chương trình này, đầu ra của chúng tôi thay đổi đáng kể. Thay vì mất đầy đủ 6 hoặc 7 giây, tập lệnh của chúng tôi hiện chạy trong 4 giây, khoảng thời gian của vòng lặp dài nhất của chúng tôi, cộng với bất kỳ chi phí nào

$ mtsleepA.py
starting at: Sun Aug 13 05:04:50 2006
start loop 0 at: Sun Aug 13 05:04:50 2006
start loop 1 at: Sun Aug 13 05:04:50 2006
loop 1 done at: Sun Aug 13 05:04:52 2006
loop 0 done at: Sun Aug 13 05:04:54 2006
all DONE at: Sun Aug 13 05:04:56 2006

Các đoạn mã ngủ trong 4 và 2 giây hiện xảy ra đồng thời, góp phần làm giảm thời gian chạy tổng thể. Bạn thậm chí có thể thấy cách vòng lặp 1 kết thúc trước vòng lặp 0

Thay đổi lớn duy nhất khác đối với ứng dụng của chúng tôi là việc bổ sung lệnh gọi sleep(6). Tại sao điều này là cần thiết?

Chúng tôi không có bất kỳ mã nào hướng luồng chính đợi các luồng con hoàn thành trước khi tiếp tục. Đây là những gì chúng tôi muốn nói bởi các chủ đề yêu cầu một số loại đồng bộ hóa. Trong trường hợp của chúng tôi, chúng tôi đã sử dụng một cuộc gọi sleep() khác làm cơ chế đồng bộ hóa của mình. Chúng tôi đã sử dụng giá trị là 6 giây vì chúng tôi biết rằng cả hai luồng (mất 4 và 2 giây) phải hoàn thành vào thời điểm luồng chính đếm đến 6

Có thể bạn đang nghĩ rằng nên có cách quản lý luồng tốt hơn là tạo thêm độ trễ 6 giây trong luồng chính. Do sự chậm trễ này, thời gian chạy tổng thể không tốt hơn trong phiên bản đơn luồng của chúng tôi. Sử dụng chế độ ngủ () để đồng bộ hóa luồng như chúng tôi đã làm là không đáng tin cậy. Điều gì sẽ xảy ra nếu các vòng lặp của chúng tôi có thời gian thực hiện độc lập và thay đổi? . Đây là nơi khóa đi vào

Thực hiện một bản cập nhật khác cho mã của chúng tôi để bao gồm các khóa cũng như loại bỏ các hàm vòng lặp riêng biệt, chúng tôi nhận được mtsleepB. py, được trình bày trong Ví dụ 4-3. Chạy thử ta thấy đầu ra tương tự như mtsleepA. py. Sự khác biệt duy nhất là chúng tôi không phải đợi thêm thời gian cho mtsleepA. py để kết luận. Bằng cách sử dụng khóa, chúng tôi có thể thoát ngay khi cả hai luồng đã hoàn thành thực thi. Điều này làm cho đầu ra sau

$ mtsleepB.py
starting at: Sun Aug 13 16:34:41 2006
start loop 0 at: Sun Aug 13 16:34:41 2006
start loop 1 at: Sun Aug 13 16:34:41 2006
loop 1 done at: Sun Aug 13 16:34:43 2006
loop 0 done at: Sun Aug 13 16:34:45 2006
all DONE at: Sun Aug 13 16:34:45 2006

Ví dụ 4-3. Sử dụng luồng và Khóa (mtsleepB. py)

Thay vì sử dụng lời gọi đến chế độ ngủ () để giữ luồng chính như trong mtsleepA. py, việc sử dụng khóa có ý nghĩa hơn

1    #!/usr/bin/env python
2
3    import thread
4    from time import sleep, ctime
5
6    loops = [4,2]
7
8    def loop(nloop, nsec, lock):
9        print 'start loop', nloop, 'at:', ctime()
10       sleep(nsec)
11       print 'loop', nloop, 'done at:', ctime()
12       lock.release()
13
14      def main():
15          print 'starting at:', ctime()
16          locks = []
17          nloops = range(len(loops))
18
19       for i in nloops:
20           lock = thread.allocate_lock()
21           lock.acquire()
22           locks.append(lock)
23
24    for i in nloops:
25        thread.start_new_thread(loop,
26            (i, loops[i], locks[i]))
27
28       for i in nloops:
29           while locks[i].locked(): pass
30
31       print 'all DONE at:', ctime()
32
33   if __name__ == '__main__':
34       main()

Vậy chúng ta đã hoàn thành nhiệm vụ của mình với ổ khóa như thế nào?

Giải thích từng dòng

Dòng 1–6

Sau dòng khởi động Unix, chúng tôi nhập mô-đun luồng và một vài thuộc tính quen thuộc của mô-đun thời gian. Thay vì mã hóa cứng các hàm riêng biệt để đếm đến 4 và 2 giây, chúng tôi sử dụng một hàm loop() duy nhất và đặt các hằng số này vào một danh sách, các vòng lặp

Dòng 8–12

Hàm loop() hoạt động như một proxy cho các hàm loop*() đã bị xóa khỏi các ví dụ trước của chúng tôi. Chúng tôi đã phải thực hiện một số thay đổi thẩm mỹ đối với loop() để giờ đây nó có thể thực hiện nhiệm vụ của mình bằng cách sử dụng khóa. Những thay đổi rõ ràng là chúng ta cần được cho biết chúng ta đang ở số vòng lặp nào cũng như thời lượng ngủ. Phần cuối cùng của thông tin mới là khóa. Mỗi luồng sẽ được phân bổ một khóa thu được. Khi thời gian ngủ () kết thúc, chúng tôi giải phóng khóa tương ứng, cho luồng chính biết rằng luồng này đã hoàn thành

Dòng 14–34

Phần lớn công việc được thực hiện ở đây trong hàm main(), sử dụng ba vòng lặp for riêng biệt. Trước tiên, chúng tôi tạo một danh sách các khóa mà chúng tôi có được bằng cách sử dụng luồng. hàmphân bổ_lock() và thu nhận (từng khóa) bằng phương thức thu thập(). Có được khóa có tác dụng “khóa khóa. ” Sau khi khóa, chúng tôi thêm khóa vào danh sách khóa, khóa. Vòng lặp tiếp theo thực sự sinh ra các luồng, gọi hàm loop() cho mỗi luồng và đối với mỗi luồng, cung cấp cho nó số vòng lặp, thời lượng ngủ và khóa thu được cho luồng đó. Vậy tại sao chúng ta không bắt đầu các luồng trong vòng thu thập khóa? . Đầu tiên, chúng tôi muốn đồng bộ hóa các chuỗi, để tất cả những con ngựa bắt đầu ra khỏi cổng cùng một lúc và thứ hai, khóa sẽ mất một chút thời gian để lấy được. Nếu chuỗi của bạn thực thi quá nhanh, có thể chuỗi đó sẽ hoàn thành trước khi có cơ hội nhận được khóa

Tùy thuộc vào mỗi luồng để mở khóa đối tượng khóa của nó khi nó đã hoàn thành thực thi. Vòng lặp cuối cùng chỉ ngồi và quay (tạm dừng luồng chính) cho đến khi cả hai khóa được giải phóng trước khi tiếp tục thực hiện. Bởi vì chúng tôi đang kiểm tra từng khóa một cách tuần tự, chúng tôi có thể phụ thuộc vào tất cả các vòng lặp chậm hơn nếu chúng ở nhiều hơn về phần đầu của tập hợp các vòng lặp. Trong những trường hợp như vậy, phần lớn thời gian chờ đợi có thể dành cho (các) vòng lặp đầu tiên. Khi khóa đó được giải phóng, các khóa còn lại có thể đã được mở khóa (có nghĩa là các luồng tương ứng đã hoàn thành thực thi). Kết quả là luồng chính sẽ lướt qua các lần kiểm tra khóa đó mà không cần tạm dừng. Cuối cùng, bạn nên biết rõ rằng cặp dòng cuối cùng sẽ chỉ thực thi hàm main() nếu chúng ta đang gọi trực tiếp tập lệnh này

Như đã gợi ý trong Ghi chú cốt lõi trước đó, chúng tôi đã trình bày mô-đun luồng chỉ để giới thiệu cho người đọc về lập trình luồng. Ứng dụng MT của bạn nên sử dụng các mô-đun cấp cao hơn, chẳng hạn như mô-đun phân luồng, mà chúng ta sẽ thảo luận trong phần tiếp theo

Các mô-đun luồng trong Python là gì?

Mô-đun luồng. Phương thức này bắt đầu một chuỗi mới và trả về mã định danh của chuỗi đó . Chuỗi thực thi hàm "hàm" (hàm là tham chiếu đến hàm) với danh sách đối số args (phải là danh sách hoặc bộ dữ liệu). Đối số kwargs tùy chọn chỉ định một từ điển các đối số từ khóa.

Mô-đun nào được sử dụng để đa luồng trong Python?

Mô-đun phân luồng . Mô-đun luồng là một triển khai đa luồng cấp cao được sử dụng để triển khai một ứng dụng trong Python. Để sử dụng đa luồng, chúng ta cần nhập mô-đun luồng trong Chương trình Python.

Các loại chủ đề trong Python là gì?

Có hai loại chủ đề riêng biệt. đó là. Chủ đề cấp người dùng. Đây là những thứ chúng ta có thể tích cực chơi với mã của mình, v.v. Luồng cấp hạt nhân. Đây là những luồng cấp rất thấp hoạt động thay mặt cho hệ điều hành .

Sự kiện luồng trong Python là gì?

Một luồng. Đối tượng sự kiện bao bọc một biến boolean có thể là "đặt" (Đúng) hoặc "không đặt" (Sai) . Các chuỗi chia sẻ phiên bản sự kiện có thể kiểm tra xem sự kiện đã được đặt chưa, đặt sự kiện, xóa sự kiện (không đặt sự kiện) hoặc đợi sự kiện được đặt.