Làm thế nào để bộ đếm thời gian chủ đề làm việc python?

Các đối tượng bộ đếm thời gian được tạo bằng cách sử dụng lớp

x = threading.Thread[target=thread_function, args=[1,], daemon=True]
47 là lớp con của lớp
x = threading.Thread[target=thread_function, args=[1,], daemon=True]
48. Sử dụng lớp này, chúng ta có thể đặt độ trễ cho bất kỳ hành động nào chỉ được chạy sau một khoảng thời gian nhất định đã trôi qua [bộ đếm thời gian] và có thể dễ dàng bị hủy trong thời gian trễ đó

Bộ hẹn giờ được bắt đầu, giống như các chuỗi bình thường, bằng cách gọi phương thức

x = threading.Thread[target=thread_function, args=[1,], daemon=True]
49 của chúng. Có thể dừng chuỗi hẹn giờ [trước khi hành động của nó bắt đầu] bằng cách gọi phương thức
x = threading.Thread[target=thread_function, args=[1,], daemon=True]
50 của nó

Đối tượng hẹn giờ thường được sử dụng để thực hiện các tác vụ theo lịch trình được cho là chỉ được thực hiện sau một khoảng thời gian nhất định

Ngoài ra, không nhất thiết đối tượng Hẹn giờ sẽ được thực thi chính xác sau thời gian đã lên lịch vì sau đó, trình hướng dẫn python tìm kiếm một luồng để thực thi tác vụ đối tượng hẹn giờ, nếu không có sẵn sẽ dẫn đến nhiều thời gian chờ đợi hơn

Cú pháp tạo đối tượng Timer

Sau đây là cú pháp cho hàm tạo của lớp

x = threading.Thread[target=thread_function, args=[1,], daemon=True]
47

threading.Timer[interval, function, args=[], kwargs={}]

Bằng cách này, chúng ta có thể tạo một đối tượng hẹn giờ sẽ chạy hàm với đối số

x = threading.Thread[target=thread_function, args=[1,], daemon=True]
52 và đối số từ khóa
x = threading.Thread[target=thread_function, args=[1,], daemon=True]
53, sau khi
x = threading.Thread[target=thread_function, args=[1,], daemon=True]
54 giây trôi qua

Các phương thức của lớp
x = threading.Thread[target=thread_function, args=[1,], daemon=True]
47

Trong lớp

x = threading.Thread[target=thread_function, args=[1,], daemon=True]
47, chúng ta có hai phương thức được sử dụng để bắt đầu và hủy bỏ việc thực thi đối tượng hẹn giờ


phương pháp
x = threading.Thread[target=thread_function, args=[1,], daemon=True]
49

Phương thức này được sử dụng để bắt đầu thực hiện đối tượng hẹn giờ. Khi chúng ta gọi phương thức này, thì đối tượng hẹn giờ bắt đầu hẹn giờ của nó


Phương pháp
x = threading.Thread[target=thread_function, args=[1,], daemon=True]
50

Phương thức này được sử dụng để dừng bộ đếm thời gian và hủy bỏ việc thực hiện hành động của đối tượng bộ đếm thời gian. Điều này sẽ chỉ hoạt động nếu bộ hẹn giờ chưa thực hiện hành động của nó

Thời gian cho một ví dụ

Dưới đây chúng tôi có một ví dụ đơn giản nơi chúng tôi tạo một đối tượng hẹn giờ và khởi động nó

import threading

def task[]:
    print["timer object task running..."]

if __name__=='__main__':
    t = threading.Timer[10, task]
    t.start[] # after 10 seconds, task will be executed

Chương trình trên là một chương trình đơn giản, bây giờ hãy sử dụng phương thức hủy để hủy thực thi tác vụ của đối tượng hẹn giờ

Trong chương trình trên, trước tiên hãy ghi chú mã trên dòng số 13 và 14 và chạy chương trình, sau đó bỏ ghi chú các dòng đó và xem phương thức

x = threading.Thread[target=thread_function, args=[1,], daemon=True]
50 hoạt độ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

Trong bài viết này, bạn sẽ học

  • chủ đề là gì
  • Cách tạo chủ đề và đợi chúng hoàn thành
  • Cách sử dụng
    x = threading.Thread[target=thread_function, args=[1,], daemon=True]
    
    5
  • Làm thế nào để tránh điều kiện chủng tộc
  • Cách sử dụng các công cụ phổ biến mà Python
    x = threading.Thread[target=thread_function, args=[1,], daemon=True]
    
    6 cung cấp

Bài viết này giả định rằng bạn đã có kiến ​​thức cơ bản về Python và bạn đang sử dụng ít nhất phiên bản 3. 6 để chạy các ví dụ. Nếu cần ôn lại, bạn có thể bắt đầu với Lộ trình học Python và bắt kịp tốc độ

Nếu bạn không chắc mình muốn sử dụng Python

x = threading.Thread[target=thread_function, args=[1,], daemon=True]
6,
x = threading.Thread[target=thread_function, args=[1,], daemon=True]
8 hay
x = threading.Thread[target=thread_function, args=[1,], daemon=True]
9, thì bạn có thể xem bài viết Tăng tốc chương trình Python của bạn bằng đồng thời

Tất cả các nguồn được sử dụng trong hướng dẫn này đều có sẵn cho bạn trong repo Real Python GitHub

Tiền thưởng miễn phí. 5 Suy nghĩ về Làm chủ Python, một khóa học miễn phí dành cho các nhà phát triển Python cho bạn thấy lộ trình và tư duy mà bạn sẽ cần để đưa các kỹ năng Python của mình lên một tầm cao mới

Lấy bài kiểm tra. Kiểm tra kiến ​​thức của bạn với bài kiểm tra tương tác “Python Threading” của chúng tôi. Sau khi hoàn thành, bạn sẽ nhận được điểm số để có thể theo dõi quá trình học tập của mình theo thời gian

Lấy bài kiểm tra "

Chủ đề là gì?

Một luồng là một luồng thực thi riêng biệt. Điều này có nghĩa là chương trình của bạn sẽ có hai việc xảy ra cùng một lúc. Nhưng đối với hầu hết các triển khai Python 3, các luồng khác nhau không thực sự thực thi cùng một lúc. họ chỉ xuất hiện để

Thật hấp dẫn khi nghĩ về phân luồng giống như có hai [hoặc nhiều] bộ xử lý khác nhau chạy trên chương trình của bạn, mỗi bộ xử lý thực hiện một nhiệm vụ độc lập cùng một lúc. Điều đó gần đúng. Các luồng có thể đang chạy trên các bộ xử lý khác nhau, nhưng chúng sẽ chỉ chạy một luồng tại một thời điểm

Để nhiều tác vụ chạy đồng thời yêu cầu triển khai Python không chuẩn, viết một số mã của bạn bằng ngôn ngữ khác hoặc sử dụng

x = threading.Thread[target=thread_function, args=[1,], daemon=True]
9 đi kèm với một số chi phí bổ sung

Do cách triển khai CPython của Python hoạt động, luồng có thể không tăng tốc tất cả các tác vụ. Điều này là do các tương tác với GIL về cơ bản giới hạn một chuỗi Python chạy tại một thời điểm

Các tác vụ dành nhiều thời gian chờ đợi các sự kiện bên ngoài thường là những ứng cử viên tốt cho luồng. Các sự cố yêu cầu tính toán CPU nặng và dành ít thời gian chờ đợi các sự kiện bên ngoài có thể không chạy nhanh hơn

Điều này đúng với mã được viết bằng Python và chạy trên triển khai CPython tiêu chuẩn. Nếu chủ đề của bạn được viết bằng C, chúng có khả năng giải phóng GIL và chạy đồng thời. Nếu bạn đang chạy trên một triển khai Python khác, hãy kiểm tra cả tài liệu để xem nó xử lý các luồng như thế nào

Nếu bạn đang chạy một triển khai Python tiêu chuẩn, chỉ viết bằng Python và gặp sự cố liên quan đến CPU, bạn nên xem mô-đun

x = threading.Thread[target=thread_function, args=[1,], daemon=True]
9 để thay thế

Kiến trúc chương trình của bạn để sử dụng phân luồng cũng có thể mang lại sự rõ ràng về thiết kế. Hầu hết các ví dụ bạn sẽ tìm hiểu trong hướng dẫn này không nhất thiết phải chạy nhanh hơn vì chúng sử dụng các luồng. Sử dụng luồng trong chúng giúp làm cho thiết kế sạch hơn và dễ suy luận hơn

Vì vậy, hãy ngừng nói về luồng và bắt đầu sử dụng nó

Loại bỏ các quảng cáo

Bắt đầu một chủ đề

Bây giờ bạn đã có ý tưởng về chủ đề là gì, hãy tìm hiểu cách tạo một chủ đề. Thư viện chuẩn của Python cung cấp

x = threading.Thread[target=thread_function, args=[1,], daemon=True]
6, chứa hầu hết các nguyên hàm mà bạn sẽ thấy trong bài viết này.
import threading

def task[]:
    print["timer object task running..."]

if __name__=='__main__':
    t = threading.Timer[10, task]
    t.start[] # after 10 seconds, task will be executed
03, trong mô-đun này, gói gọn các luồng một cách độc đáo, cung cấp giao diện rõ ràng để làm việc với chúng

Để bắt đầu một chuỗi riêng biệt, bạn tạo một phiên bản

import threading

def task[]:
    print["timer object task running..."]

if __name__=='__main__':
    t = threading.Timer[10, task]
    t.start[] # after 10 seconds, task will be executed
03 và sau đó yêu cầu nó gửi cho
import threading

def task[]:
    print["timer object task running..."]

if __name__=='__main__':
    t = threading.Timer[10, task]
    t.start[] # after 10 seconds, task will be executed
05

import threading

def task[]:
    print["timer object task running..."]

if __name__=='__main__':
    t = threading.Timer[10, task]
    t.start[] # after 10 seconds, task will be executed
3

Nếu bạn nhìn xung quanh các câu lệnh ghi nhật ký, bạn có thể thấy rằng phần

import threading

def task[]:
    print["timer object task running..."]

if __name__=='__main__':
    t = threading.Timer[10, task]
    t.start[] # after 10 seconds, task will be executed
06 đang tạo và bắt đầu chuỗi

import threading

def task[]:
    print["timer object task running..."]

if __name__=='__main__':
    t = threading.Timer[10, task]
    t.start[] # after 10 seconds, task will be executed
5

Khi bạn tạo một

import threading

def task[]:
    print["timer object task running..."]

if __name__=='__main__':
    t = threading.Timer[10, task]
    t.start[] # after 10 seconds, task will be executed
03, bạn truyền cho nó một hàm và một danh sách chứa các đối số của hàm đó. Trong trường hợp này, bạn đang yêu cầu
import threading

def task[]:
    print["timer object task running..."]

if __name__=='__main__':
    t = threading.Timer[10, task]
    t.start[] # after 10 seconds, task will be executed
03 chạy
import threading

def task[]:
    print["timer object task running..."]

if __name__=='__main__':
    t = threading.Timer[10, task]
    t.start[] # after 10 seconds, task will be executed
09 và chuyển nó
import threading

def task[]:
    print["timer object task running..."]

if __name__=='__main__':
    t = threading.Timer[10, task]
    t.start[] # after 10 seconds, task will be executed
90 làm đối số

Đối với bài viết này, bạn sẽ sử dụng các số nguyên tuần tự làm tên cho chủ đề của mình. Có

import threading

def task[]:
    print["timer object task running..."]

if __name__=='__main__':
    t = threading.Timer[10, task]
    t.start[] # after 10 seconds, task will be executed
91, trả về một tên duy nhất cho mỗi luồng, nhưng những tên này thường không ngắn và cũng không dễ đọc

Bản thân

import threading

def task[]:
    print["timer object task running..."]

if __name__=='__main__':
    t = threading.Timer[10, task]
    t.start[] # after 10 seconds, task will be executed
09 không làm được gì nhiều. Nó chỉ đơn giản là ghi lại một số tin nhắn với một
import threading

def task[]:
    print["timer object task running..."]

if __name__=='__main__':
    t = threading.Timer[10, task]
    t.start[] # after 10 seconds, task will be executed
93 ở giữa chúng

Khi bạn chạy chương trình này như hiện tại [với dòng 20 được chú thích], đầu ra sẽ như thế này

x = threading.Thread[target=thread_function, args=[1,], daemon=True]
3

Bạn sẽ nhận thấy rằng

import threading

def task[]:
    print["timer object task running..."]

if __name__=='__main__':
    t = threading.Timer[10, task]
    t.start[] # after 10 seconds, task will be executed
03 đã hoàn thành sau khi phần
import threading

def task[]:
    print["timer object task running..."]

if __name__=='__main__':
    t = threading.Timer[10, task]
    t.start[] # after 10 seconds, task will be executed
95 trong mã của bạn đã hoàn thành. Bạn sẽ quay lại với lý do tại sao lại như vậy và nói về dòng số 20 bí ẩn trong phần tiếp theo

chủ đề daemon

Trong khoa học máy tính, một

import threading

def task[]:
    print["timer object task running..."]

if __name__=='__main__':
    t = threading.Timer[10, task]
    t.start[] # after 10 seconds, task will be executed
96 là một quá trình chạy trong nền

Python

x = threading.Thread[target=thread_function, args=[1,], daemon=True]
6 có ý nghĩa cụ thể hơn cho
import threading

def task[]:
    print["timer object task running..."]

if __name__=='__main__':
    t = threading.Timer[10, task]
    t.start[] # after 10 seconds, task will be executed
96. Chuỗi
import threading

def task[]:
    print["timer object task running..."]

if __name__=='__main__':
    t = threading.Timer[10, task]
    t.start[] # after 10 seconds, task will be executed
96 sẽ tắt ngay lập tức khi thoát khỏi chương trình. Một cách để suy nghĩ về những định nghĩa này là coi luồng
import threading

def task[]:
    print["timer object task running..."]

if __name__=='__main__':
    t = threading.Timer[10, task]
    t.start[] # after 10 seconds, task will be executed
96 là luồng chạy trong nền mà không cần lo lắng về việc tắt nó

Nếu một chương trình đang chạy

x = threading.Thread[target=thread_function, args=[1,], daemon=True]
11 mà không phải là
x = threading.Thread[target=thread_function, args=[1,], daemon=True]
12, thì chương trình sẽ đợi các luồng đó hoàn thành trước khi kết thúc. Tuy nhiên,
x = threading.Thread[target=thread_function, args=[1,], daemon=True]
11 là daemon, chỉ bị giết ở bất cứ đâu khi chương trình đang thoát

Hãy xem xét kỹ hơn một chút đầu ra của chương trình của bạn ở trên. Hai dòng cuối cùng là một chút thú vị. Khi bạn chạy chương trình, bạn sẽ nhận thấy rằng có một khoảng dừng [khoảng 2 giây] sau khi

x = threading.Thread[target=thread_function, args=[1,], daemon=True]
14 đã in thông báo
x = threading.Thread[target=thread_function, args=[1,], daemon=True]
15 của nó và trước khi luồng kết thúc

Việc tạm dừng này là Python đang chờ chuỗi không phải daemon hoàn thành. Khi chương trình Python của bạn kết thúc, một phần của quy trình tắt máy là dọn sạch quy trình luồng

Nếu bạn nhìn vào , bạn sẽ thấy rằng

x = threading.Thread[target=thread_function, args=[1,], daemon=True]
17 đi qua tất cả các luồng đang chạy và gọi
x = threading.Thread[target=thread_function, args=[1,], daemon=True]
18 trên mọi luồng không có cờ
import threading

def task[]:
    print["timer object task running..."]

if __name__=='__main__':
    t = threading.Timer[10, task]
    t.start[] # after 10 seconds, task will be executed
96 được đặt

Vì vậy, chương trình của bạn chờ để thoát vì bản thân luồng đang chờ trong chế độ ngủ. Ngay sau khi hoàn thành và in thông báo,

x = threading.Thread[target=thread_function, args=[1,], daemon=True]
18 sẽ quay trở lại và chương trình có thể thoát

Thông thường, hành vi này là những gì bạn muốn, nhưng có các tùy chọn khác có sẵn cho chúng tôi. Trước tiên, hãy lặp lại chương trình với luồng

import threading

def task[]:
    print["timer object task running..."]

if __name__=='__main__':
    t = threading.Timer[10, task]
    t.start[] # after 10 seconds, task will be executed
96. Bạn làm điều đó bằng cách thay đổi cách bạn xây dựng
import threading

def task[]:
    print["timer object task running..."]

if __name__=='__main__':
    t = threading.Timer[10, task]
    t.start[] # after 10 seconds, task will be executed
03, thêm cờ
x = threading.Thread[target=thread_function, args=[1,], daemon=True]
23

x = threading.Thread[target=thread_function, args=[1,], daemon=True]

Khi bạn chạy chương trình bây giờ, bạn sẽ thấy đầu ra này

import threading

def task[]:
    print["timer object task running..."]

if __name__=='__main__':
    t = threading.Timer[10, task]
    t.start[] # after 10 seconds, task will be executed
0

Sự khác biệt ở đây là dòng cuối cùng của đầu ra bị thiếu.

import threading

def task[]:
    print["timer object task running..."]

if __name__=='__main__':
    t = threading.Timer[10, task]
    t.start[] # after 10 seconds, task will be executed
09 không có cơ hội hoàn thành. Đó là một chuỗi
import threading

def task[]:
    print["timer object task running..."]

if __name__=='__main__':
    t = threading.Timer[10, task]
    t.start[] # after 10 seconds, task will be executed
96, vì vậy khi
x = threading.Thread[target=thread_function, args=[1,], daemon=True]
14 đến cuối mã của nó và chương trình muốn kết thúc, daemon đã bị giết

Loại bỏ các quảng cáo

x = threading.Thread[target=thread_function, args=[1,], daemon=True]
27 một chủ đề

Chủ đề daemon rất tiện dụng, nhưng còn khi bạn muốn đợi một chủ đề dừng lại thì sao?

import threading

def task[]:
    print["timer object task running..."]

if __name__=='__main__':
    t = threading.Timer[10, task]
    t.start[] # after 10 seconds, task will be executed
9

Để yêu cầu một luồng đợi một luồng khác kết thúc, bạn gọi _

x = threading.Thread[target=thread_function, args=[1,], daemon=True]
18. Nếu bạn bỏ ghi chú dòng đó, luồng chính sẽ tạm dừng và đợi luồng
x = threading.Thread[target=thread_function, args=[1,], daemon=True]
29 chạy hoàn tất

Bạn đã kiểm tra điều này trên mã bằng luồng daemon hay luồng thông thường chưa? . Nếu bạn

x = threading.Thread[target=thread_function, args=[1,], daemon=True]
18 một chủ đề, câu lệnh đó sẽ đợi cho đến khi một trong hai loại chủ đề kết thúc

Làm việc với nhiều chủ đề

Mã ví dụ cho đến nay chỉ hoạt động với hai luồng. chủ đề chính và một chủ đề bạn đã bắt đầu với đối tượng

x = threading.Thread[target=thread_function, args=[1,], daemon=True]
31

Thông thường, bạn sẽ muốn bắt đầu một số chủ đề và để chúng thực hiện công việc thú vị. Hãy bắt đầu bằng cách xem xét cách khó hơn để thực hiện điều đó, sau đó bạn sẽ chuyển sang phương pháp dễ dàng hơn

Cách khó hơn để bắt đầu nhiều chủ đề là cách bạn đã biết

x = threading.Thread[target=thread_function, args=[1,], daemon=True]
1

Mã này sử dụng cùng một cơ chế mà bạn đã thấy ở trên để bắt đầu một chuỗi, tạo một đối tượng

import threading

def task[]:
    print["timer object task running..."]

if __name__=='__main__':
    t = threading.Timer[10, task]
    t.start[] # after 10 seconds, task will be executed
03, sau đó gọi
import threading

def task[]:
    print["timer object task running..."]

if __name__=='__main__':
    t = threading.Timer[10, task]
    t.start[] # after 10 seconds, task will be executed
05. Chương trình giữ một danh sách các đối tượng
import threading

def task[]:
    print["timer object task running..."]

if __name__=='__main__':
    t = threading.Timer[10, task]
    t.start[] # after 10 seconds, task will be executed
03 để có thể chờ chúng sau này bằng cách sử dụng
x = threading.Thread[target=thread_function, args=[1,], daemon=True]
18

Chạy mã này nhiều lần sẽ có thể tạo ra một số kết quả thú vị. Đây là một ví dụ đầu ra từ máy của tôi

x = threading.Thread[target=thread_function, args=[1,], daemon=True]
2

Nếu xem kỹ đầu ra, bạn sẽ thấy cả ba luồng bắt đầu theo thứ tự bạn có thể mong đợi, nhưng trong trường hợp này, chúng kết thúc theo thứ tự ngược lại. Nhiều lần chạy sẽ tạo ra các thứ tự khác nhau. Tìm thông báo

x = threading.Thread[target=thread_function, args=[1,], daemon=True]
36 để cho bạn biết khi mỗi chủ đề hoàn thành

Thứ tự chạy các luồng được xác định bởi hệ điều hành và có thể khá khó dự đoán. Nó có thể [và có khả năng sẽ] khác nhau giữa các lần chạy, vì vậy bạn cần lưu ý điều đó khi thiết kế các thuật toán sử dụng phân luồng

May mắn thay, Python cung cấp cho bạn một số nguyên hàm mà bạn sẽ xem xét sau này để giúp phối hợp các luồng và khiến chúng chạy cùng nhau. Trước đó, hãy xem cách quản lý một nhóm chủ đề dễ dàng hơn một chút

Sử dụng một
x = threading.Thread[target=thread_function, args=[1,], daemon=True]
5

Có một cách dễ dàng hơn để bắt đầu một nhóm chủ đề so với cách bạn đã thấy ở trên. Nó được gọi là

x = threading.Thread[target=thread_function, args=[1,], daemon=True]
5 và là một phần của thư viện chuẩn trong
x = threading.Thread[target=thread_function, args=[1,], daemon=True]
39 [kể từ Python 3. 2]

Cách dễ nhất để tạo nó là với tư cách là người quản lý bối cảnh, sử dụng câu lệnh

x = threading.Thread[target=thread_function, args=[1,], daemon=True]
40 để quản lý việc tạo và hủy nhóm

Đây là

x = threading.Thread[target=thread_function, args=[1,], daemon=True]
14 từ ví dụ cuối cùng được viết lại để sử dụng
x = threading.Thread[target=thread_function, args=[1,], daemon=True]
5

x = threading.Thread[target=thread_function, args=[1,], daemon=True]
3

Đoạn mã tạo một

x = threading.Thread[target=thread_function, args=[1,], daemon=True]
5 làm trình quản lý bối cảnh, cho biết nó muốn có bao nhiêu luồng công nhân trong nhóm. Sau đó, nó sử dụng ____644 để chuyển qua một thứ có thể lặp lại, trong trường hợp của bạn là ____645, chuyển từng thứ tới một luồng trong nhóm

Sự kết thúc của khối

x = threading.Thread[target=thread_function, args=[1,], daemon=True]
40 làm cho
x = threading.Thread[target=thread_function, args=[1,], daemon=True]
5 thực hiện một
x = threading.Thread[target=thread_function, args=[1,], daemon=True]
18 trên mỗi luồng trong nhóm. Chúng tôi thực sự khuyên bạn nên sử dụng
x = threading.Thread[target=thread_function, args=[1,], daemon=True]
5 làm trình quản lý ngữ cảnh khi có thể để bạn không bao giờ quên
x = threading.Thread[target=thread_function, args=[1,], daemon=True]
18 chủ đề

Ghi chú. Sử dụng

x = threading.Thread[target=thread_function, args=[1,], daemon=True]
5 có thể gây ra một số lỗi khó hiểu

Ví dụ: nếu bạn gọi một hàm không có tham số, nhưng bạn truyền tham số cho nó trong

x = threading.Thread[target=thread_function, args=[1,], daemon=True]
44, luồng sẽ đưa ra một ngoại lệ

Thật không may,

x = threading.Thread[target=thread_function, args=[1,], daemon=True]
5 sẽ ẩn ngoại lệ đó và [trong trường hợp trên] chương trình kết thúc mà không có đầu ra. Điều này có thể khá khó hiểu khi gỡ lỗi lúc đầu

Chạy mã ví dụ đã sửa của bạn sẽ tạo ra kết quả giống như thế này

x = threading.Thread[target=thread_function, args=[1,], daemon=True]
4

Một lần nữa, hãy chú ý cách

import threading

def task[]:
    print["timer object task running..."]

if __name__=='__main__':
    t = threading.Timer[10, task]
    t.start[] # after 10 seconds, task will be executed
504 hoàn thành trước
import threading

def task[]:
    print["timer object task running..."]

if __name__=='__main__':
    t = threading.Timer[10, task]
    t.start[] # after 10 seconds, task will be executed
505. Việc lập lịch trình cho các luồng được thực hiện bởi hệ điều hành và không tuân theo một kế hoạch dễ hình dung

Loại bỏ các quảng cáo

Điều kiện cuộc đua

Trước khi bạn chuyển sang một số tính năng khác có trong Python

x = threading.Thread[target=thread_function, args=[1,], daemon=True]
6, hãy nói một chút về một trong những vấn đề khó khăn hơn mà bạn sẽ gặp phải khi viết các chương trình theo luồng. điều kiện cuộc đua

Sau khi bạn đã biết điều kiện chủng tộc là gì và xem xét điều kiện xảy ra, bạn sẽ chuyển sang một số nguyên tắc do thư viện tiêu chuẩn cung cấp để ngăn chặn điều kiện chủng tộc xảy ra

Điều kiện cạnh tranh có thể xảy ra khi hai hoặc nhiều luồng truy cập vào một phần dữ liệu hoặc tài nguyên được chia sẻ. Trong ví dụ này, bạn sẽ tạo một điều kiện cuộc đua lớn xảy ra mọi lúc, nhưng lưu ý rằng hầu hết các điều kiện cuộc đua không rõ ràng như vậy. Thông thường, chúng chỉ hiếm khi xảy ra và chúng có thể tạo ra kết quả khó hiểu. Như bạn có thể tưởng tượng, điều này khiến chúng khá khó gỡ lỗi

May mắn thay, điều kiện cuộc đua này sẽ xảy ra mọi lúc và bạn sẽ xem chi tiết về nó để giải thích điều gì đang xảy ra

Trong ví dụ này, bạn sẽ viết một lớp cập nhật cơ sở dữ liệu. Được rồi, bạn sẽ không thực sự có một cơ sở dữ liệu. bạn sẽ giả mạo nó, bởi vì đó không phải là điểm của bài viết này

import threading

def task[]:
    print["timer object task running..."]

if __name__=='__main__':
    t = threading.Timer[10, task]
    t.start[] # after 10 seconds, task will be executed
507 của bạn sẽ có các phương thức
import threading

def task[]:
    print["timer object task running..."]

if __name__=='__main__':
    t = threading.Timer[10, task]
    t.start[] # after 10 seconds, task will be executed
508 và
import threading

def task[]:
    print["timer object task running..."]

if __name__=='__main__':
    t = threading.Timer[10, task]
    t.start[] # after 10 seconds, task will be executed
509

import threading

def task[]:
    print["timer object task running..."]

if __name__=='__main__':
    t = threading.Timer[10, task]
    t.start[] # after 10 seconds, task will be executed
50

import threading

def task[]:
    print["timer object task running..."]

if __name__=='__main__':
    t = threading.Timer[10, task]
    t.start[] # after 10 seconds, task will be executed
507 đang theo dõi một số duy nhất.
import threading

def task[]:
    print["timer object task running..."]

if __name__=='__main__':
    t = threading.Timer[10, task]
    t.start[] # after 10 seconds, task will be executed
511. Đây sẽ là dữ liệu được chia sẻ mà bạn sẽ thấy điều kiện cuộc đua

import threading

def task[]:
    print["timer object task running..."]

if __name__=='__main__':
    t = threading.Timer[10, task]
    t.start[] # after 10 seconds, task will be executed
508 chỉ cần khởi tạo
import threading

def task[]:
    print["timer object task running..."]

if __name__=='__main__':
    t = threading.Timer[10, task]
    t.start[] # after 10 seconds, task will be executed
511 thành 0. Càng xa càng tốt

import threading

def task[]:
    print["timer object task running..."]

if __name__=='__main__':
    t = threading.Timer[10, task]
    t.start[] # after 10 seconds, task will be executed
509 nhìn hơi lạ. Nó đang mô phỏng việc đọc một giá trị từ cơ sở dữ liệu, thực hiện một số tính toán trên đó và sau đó ghi một giá trị mới trở lại cơ sở dữ liệu

Trong trường hợp này, đọc từ cơ sở dữ liệu chỉ có nghĩa là sao chép

import threading

def task[]:
    print["timer object task running..."]

if __name__=='__main__':
    t = threading.Timer[10, task]
    t.start[] # after 10 seconds, task will be executed
511 vào một biến cục bộ. Việc tính toán chỉ là thêm một vào giá trị và sau đó
import threading

def task[]:
    print["timer object task running..."]

if __name__=='__main__':
    t = threading.Timer[10, task]
    t.start[] # after 10 seconds, task will be executed
516 một chút. Cuối cùng, nó ghi lại giá trị bằng cách sao chép giá trị cục bộ trở lại
import threading

def task[]:
    print["timer object task running..."]

if __name__=='__main__':
    t = threading.Timer[10, task]
    t.start[] # after 10 seconds, task will be executed
511

Đây là cách bạn sẽ sử dụng

import threading

def task[]:
    print["timer object task running..."]

if __name__=='__main__':
    t = threading.Timer[10, task]
    t.start[] # after 10 seconds, task will be executed
507 này

import threading

def task[]:
    print["timer object task running..."]

if __name__=='__main__':
    t = threading.Timer[10, task]
    t.start[] # after 10 seconds, task will be executed
51

Chương trình tạo một

x = threading.Thread[target=thread_function, args=[1,], daemon=True]
5 với hai luồng và sau đó gọi
import threading

def task[]:
    print["timer object task running..."]

if __name__=='__main__':
    t = threading.Timer[10, task]
    t.start[] # after 10 seconds, task will be executed
520 trên mỗi luồng, yêu cầu chúng chạy
import threading

def task[]:
    print["timer object task running..."]

if __name__=='__main__':
    t = threading.Timer[10, task]
    t.start[] # after 10 seconds, task will be executed
521

import threading

def task[]:
    print["timer object task running..."]

if __name__=='__main__':
    t = threading.Timer[10, task]
    t.start[] # after 10 seconds, task will be executed
520 có một chữ ký cho phép cả đối số vị trí và đối số được đặt tên được chuyển đến hàm đang chạy trong luồng

import threading

def task[]:
    print["timer object task running..."]

if __name__=='__main__':
    t = threading.Timer[10, task]
    t.start[] # after 10 seconds, task will be executed
52

Trong cách sử dụng ở trên,

import threading

def task[]:
    print["timer object task running..."]

if __name__=='__main__':
    t = threading.Timer[10, task]
    t.start[] # after 10 seconds, task will be executed
523 được chuyển làm đối số vị trí đầu tiên và duy nhất cho
import threading

def task[]:
    print["timer object task running..."]

if __name__=='__main__':
    t = threading.Timer[10, task]
    t.start[] # after 10 seconds, task will be executed
521. Bạn sẽ thấy ở phần sau của bài viết này, nơi bạn có thể truyền nhiều đối số theo cách tương tự

Vì mỗi luồng chạy

import threading

def task[]:
    print["timer object task running..."]

if __name__=='__main__':
    t = threading.Timer[10, task]
    t.start[] # after 10 seconds, task will be executed
509 và
import threading

def task[]:
    print["timer object task running..."]

if __name__=='__main__':
    t = threading.Timer[10, task]
    t.start[] # after 10 seconds, task will be executed
509 thêm một vào
import threading

def task[]:
    print["timer object task running..."]

if __name__=='__main__':
    t = threading.Timer[10, task]
    t.start[] # after 10 seconds, task will be executed
511, nên bạn có thể mong đợi
import threading

def task[]:
    print["timer object task running..."]

if __name__=='__main__':
    t = threading.Timer[10, task]
    t.start[] # after 10 seconds, task will be executed
528 là
import threading

def task[]:
    print["timer object task running..."]

if __name__=='__main__':
    t = threading.Timer[10, task]
    t.start[] # after 10 seconds, task will be executed
529 khi nó được in ra ở cuối. Nhưng bạn sẽ không xem xét ví dụ này nếu đó là trường hợp. Nếu bạn chạy đoạn mã trên, kết quả sẽ như thế này

import threading

def task[]:
    print["timer object task running..."]

if __name__=='__main__':
    t = threading.Timer[10, task]
    t.start[] # after 10 seconds, task will be executed
53

Bạn có thể đã mong đợi điều đó xảy ra, nhưng hãy xem chi tiết về những gì đang thực sự xảy ra ở đây, vì điều đó sẽ giúp giải pháp cho vấn đề này dễ hiểu hơn

Loại bỏ các quảng cáo

một chủ đề

Trước khi bạn đi sâu vào vấn đề này với hai luồng, hãy quay lại và nói một chút về một số chi tiết về cách hoạt động của các luồng

Bạn sẽ không đi sâu vào tất cả các chi tiết ở đây, vì điều đó không quan trọng ở cấp độ này. Chúng tôi cũng sẽ đơn giản hóa một số thứ theo cách không chính xác về mặt kỹ thuật nhưng sẽ cung cấp cho bạn ý tưởng đúng về những gì đang xảy ra

Khi bạn yêu cầu

x = threading.Thread[target=thread_function, args=[1,], daemon=True]
5 của mình chạy từng luồng, bạn sẽ cho nó biết chức năng nào sẽ chạy và thông số nào cần truyền cho nó.
import threading

def task[]:
    print["timer object task running..."]

if __name__=='__main__':
    t = threading.Timer[10, task]
    t.start[] # after 10 seconds, task will be executed
531

Kết quả của việc này là mỗi luồng trong nhóm sẽ gọi

import threading

def task[]:
    print["timer object task running..."]

if __name__=='__main__':
    t = threading.Timer[10, task]
    t.start[] # after 10 seconds, task will be executed
532. Lưu ý rằng
import threading

def task[]:
    print["timer object task running..."]

if __name__=='__main__':
    t = threading.Timer[10, task]
    t.start[] # after 10 seconds, task will be executed
533 là tham chiếu đến một đối tượng
import threading

def task[]:
    print["timer object task running..."]

if __name__=='__main__':
    t = threading.Timer[10, task]
    t.start[] # after 10 seconds, task will be executed
507 được tạo trong
x = threading.Thread[target=thread_function, args=[1,], daemon=True]
14. Gọi
import threading

def task[]:
    print["timer object task running..."]

if __name__=='__main__':
    t = threading.Timer[10, task]
    t.start[] # after 10 seconds, task will be executed
509 trên đối tượng đó gọi một phương thức thể hiện trên đối tượng đó

Mỗi luồng sẽ có một tham chiếu đến cùng một đối tượng

import threading

def task[]:
    print["timer object task running..."]

if __name__=='__main__':
    t = threading.Timer[10, task]
    t.start[] # after 10 seconds, task will be executed
507,
import threading

def task[]:
    print["timer object task running..."]

if __name__=='__main__':
    t = threading.Timer[10, task]
    t.start[] # after 10 seconds, task will be executed
533. Mỗi luồng cũng sẽ có một giá trị duy nhất,
import threading

def task[]:
    print["timer object task running..."]

if __name__=='__main__':
    t = threading.Timer[10, task]
    t.start[] # after 10 seconds, task will be executed
523, để làm cho các báo cáo ghi nhật ký dễ đọc hơn một chút

Khi luồng bắt đầu chạy

import threading

def task[]:
    print["timer object task running..."]

if __name__=='__main__':
    t = threading.Timer[10, task]
    t.start[] # after 10 seconds, task will be executed
509, nó có phiên bản riêng của tất cả dữ liệu cục bộ cho hàm. Trong trường hợp của
import threading

def task[]:
    print["timer object task running..."]

if __name__=='__main__':
    t = threading.Timer[10, task]
    t.start[] # after 10 seconds, task will be executed
509, đây là
import threading

def task[]:
    print["timer object task running..."]

if __name__=='__main__':
    t = threading.Timer[10, task]
    t.start[] # after 10 seconds, task will be executed
542. Đây chắc chắn là một điều tốt. Nếu không, hai luồng chạy cùng một chức năng sẽ luôn gây nhầm lẫn cho nhau. Điều đó có nghĩa là tất cả các biến nằm trong phạm vi [hoặc cục bộ] của một hàm đều an toàn cho luồng

Bây giờ bạn có thể bắt đầu tìm hiểu điều gì sẽ xảy ra nếu bạn chạy chương trình ở trên với một chuỗi và một cuộc gọi duy nhất tới

import threading

def task[]:
    print["timer object task running..."]

if __name__=='__main__':
    t = threading.Timer[10, task]
    t.start[] # after 10 seconds, task will be executed
509

Hình ảnh dưới đây hướng dẫn thực thi

import threading

def task[]:
    print["timer object task running..."]

if __name__=='__main__':
    t = threading.Timer[10, task]
    t.start[] # after 10 seconds, task will be executed
509 nếu chỉ có một luồng duy nhất được chạy. Câu lệnh được hiển thị ở bên trái, theo sau là sơ đồ hiển thị các giá trị trong
import threading

def task[]:
    print["timer object task running..."]

if __name__=='__main__':
    t = threading.Timer[10, task]
    t.start[] # after 10 seconds, task will be executed
542 của luồng và
import threading

def task[]:
    print["timer object task running..."]

if __name__=='__main__':
    t = threading.Timer[10, task]
    t.start[] # after 10 seconds, task will be executed
528 được chia sẻ

Sơ đồ được bố trí sao cho thời gian tăng lên khi bạn di chuyển từ trên xuống dưới. Nó bắt đầu khi

import threading

def task[]:
    print["timer object task running..."]

if __name__=='__main__':
    t = threading.Timer[10, task]
    t.start[] # after 10 seconds, task will be executed
504 được tạo và kết thúc khi nó bị chấm dứt

Khi

import threading

def task[]:
    print["timer object task running..."]

if __name__=='__main__':
    t = threading.Timer[10, task]
    t.start[] # after 10 seconds, task will be executed
504 bắt đầu,
import threading

def task[]:
    print["timer object task running..."]

if __name__=='__main__':
    t = threading.Timer[10, task]
    t.start[] # after 10 seconds, task will be executed
549 bằng không. Dòng mã đầu tiên trong phương thức,
import threading

def task[]:
    print["timer object task running..."]

if __name__=='__main__':
    t = threading.Timer[10, task]
    t.start[] # after 10 seconds, task will be executed
550, sao chép giá trị 0 vào biến cục bộ. Tiếp theo, nó tăng giá trị của
import threading

def task[]:
    print["timer object task running..."]

if __name__=='__main__':
    t = threading.Timer[10, task]
    t.start[] # after 10 seconds, task will be executed
542 bằng câu lệnh
import threading

def task[]:
    print["timer object task running..."]

if __name__=='__main__':
    t = threading.Timer[10, task]
    t.start[] # after 10 seconds, task will be executed
552. Bạn có thể thấy
import threading

def task[]:
    print["timer object task running..."]

if __name__=='__main__':
    t = threading.Timer[10, task]
    t.start[] # after 10 seconds, task will be executed
511 trong
import threading

def task[]:
    print["timer object task running..."]

if __name__=='__main__':
    t = threading.Timer[10, task]
    t.start[] # after 10 seconds, task will be executed
504 được đặt thành một

Tiếp theo

import threading

def task[]:
    print["timer object task running..."]

if __name__=='__main__':
    t = threading.Timer[10, task]
    t.start[] # after 10 seconds, task will be executed
93 được gọi, làm cho luồng hiện tại tạm dừng và cho phép các luồng khác chạy. Vì chỉ có một luồng trong ví dụ này nên điều này không có hiệu lực

Khi

import threading

def task[]:
    print["timer object task running..."]

if __name__=='__main__':
    t = threading.Timer[10, task]
    t.start[] # after 10 seconds, task will be executed
504 thức dậy và tiếp tục, nó sẽ sao chép giá trị mới từ
import threading

def task[]:
    print["timer object task running..."]

if __name__=='__main__':
    t = threading.Timer[10, task]
    t.start[] # after 10 seconds, task will be executed
542 sang
import threading

def task[]:
    print["timer object task running..."]

if __name__=='__main__':
    t = threading.Timer[10, task]
    t.start[] # after 10 seconds, task will be executed
549, sau đó chuỗi hoàn thành. Bạn có thể thấy rằng
import threading

def task[]:
    print["timer object task running..."]

if __name__=='__main__':
    t = threading.Timer[10, task]
    t.start[] # after 10 seconds, task will be executed
528 được đặt thành một

Càng xa càng tốt. Bạn đã chạy

import threading

def task[]:
    print["timer object task running..."]

if __name__=='__main__':
    t = threading.Timer[10, task]
    t.start[] # after 10 seconds, task will be executed
509 một lần và
import threading

def task[]:
    print["timer object task running..."]

if __name__=='__main__':
    t = threading.Timer[10, task]
    t.start[] # after 10 seconds, task will be executed
549 đã được tăng lên một

hai chủ đề

Quay trở lại điều kiện cuộc đua, hai luồng sẽ chạy đồng thời nhưng không đồng thời. Mỗi người sẽ có phiên bản

import threading

def task[]:
    print["timer object task running..."]

if __name__=='__main__':
    t = threading.Timer[10, task]
    t.start[] # after 10 seconds, task will be executed
542 của riêng mình và mỗi người sẽ trỏ đến cùng một
import threading

def task[]:
    print["timer object task running..."]

if __name__=='__main__':
    t = threading.Timer[10, task]
    t.start[] # after 10 seconds, task will be executed
533. Chính đối tượng
import threading

def task[]:
    print["timer object task running..."]

if __name__=='__main__':
    t = threading.Timer[10, task]
    t.start[] # after 10 seconds, task will be executed
533 được chia sẻ này sẽ gây ra sự cố

Chương trình bắt đầu với ________ 1504 đang chạy ________ 1509

Khi

import threading

def task[]:
    print["timer object task running..."]

if __name__=='__main__':
    t = threading.Timer[10, task]
    t.start[] # after 10 seconds, task will be executed
504 gọi
import threading

def task[]:
    print["timer object task running..."]

if __name__=='__main__':
    t = threading.Timer[10, task]
    t.start[] # after 10 seconds, task will be executed
93, nó cho phép luồng khác bắt đầu chạy. Đây là nơi mà mọi thứ trở nên thú vị

import threading

def task[]:
    print["timer object task running..."]

if __name__=='__main__':
    t = threading.Timer[10, task]
    t.start[] # after 10 seconds, task will be executed
569 khởi động và thực hiện các thao tác tương tự. Nó cũng đang sao chép
import threading

def task[]:
    print["timer object task running..."]

if __name__=='__main__':
    t = threading.Timer[10, task]
    t.start[] # after 10 seconds, task will be executed
528 vào
import threading

def task[]:
    print["timer object task running..."]

if __name__=='__main__':
    t = threading.Timer[10, task]
    t.start[] # after 10 seconds, task will be executed
542 riêng tư của nó và
import threading

def task[]:
    print["timer object task running..."]

if __name__=='__main__':
    t = threading.Timer[10, task]
    t.start[] # after 10 seconds, task will be executed
528 được chia sẻ này vẫn chưa được cập nhật

Khi

import threading

def task[]:
    print["timer object task running..."]

if __name__=='__main__':
    t = threading.Timer[10, task]
    t.start[] # after 10 seconds, task will be executed
569 cuối cùng đi vào chế độ ngủ,
import threading

def task[]:
    print["timer object task running..."]

if __name__=='__main__':
    t = threading.Timer[10, task]
    t.start[] # after 10 seconds, task will be executed
528 được chia sẻ vẫn chưa được sửa đổi ở mức 0 và cả hai phiên bản riêng của
import threading

def task[]:
    print["timer object task running..."]

if __name__=='__main__':
    t = threading.Timer[10, task]
    t.start[] # after 10 seconds, task will be executed
542 đều có giá trị là một

import threading

def task[]:
    print["timer object task running..."]

if __name__=='__main__':
    t = threading.Timer[10, task]
    t.start[] # after 10 seconds, task will be executed
504 hiện thức dậy và lưu phiên bản
import threading

def task[]:
    print["timer object task running..."]

if __name__=='__main__':
    t = threading.Timer[10, task]
    t.start[] # after 10 seconds, task will be executed
542 của nó rồi chấm dứt, tạo cơ hội cuối cùng cho
import threading

def task[]:
    print["timer object task running..."]

if __name__=='__main__':
    t = threading.Timer[10, task]
    t.start[] # after 10 seconds, task will be executed
569 để chạy.
import threading

def task[]:
    print["timer object task running..."]

if __name__=='__main__':
    t = threading.Timer[10, task]
    t.start[] # after 10 seconds, task will be executed
569 không biết rằng ________ 1504 đã chạy và cập nhật _______ 1528 khi nó đang ngủ. Nó lưu trữ phiên bản
import threading

def task[]:
    print["timer object task running..."]

if __name__=='__main__':
    t = threading.Timer[10, task]
    t.start[] # after 10 seconds, task will be executed
542 của nó vào
import threading

def task[]:
    print["timer object task running..."]

if __name__=='__main__':
    t = threading.Timer[10, task]
    t.start[] # after 10 seconds, task will be executed
528, đồng thời đặt nó thành một

Hai luồng có quyền truy cập xen kẽ vào một đối tượng được chia sẻ duy nhất, ghi đè lên kết quả của nhau. Các điều kiện tương tự có thể phát sinh khi một luồng giải phóng bộ nhớ hoặc đóng một bộ xử lý tệp trước khi luồng kia truy cập xong.

Loại bỏ các quảng cáo

Tại sao đây không phải là một ví dụ ngớ ngẩn

Ví dụ trên được tạo ra để đảm bảo rằng điều kiện chạy đua xảy ra mỗi khi bạn chạy chương trình của mình. Bởi vì hệ điều hành có thể hoán đổi một luồng bất kỳ lúc nào, nên có thể ngắt một câu lệnh như

import threading

def task[]:
    print["timer object task running..."]

if __name__=='__main__':
    t = threading.Timer[10, task]
    t.start[] # after 10 seconds, task will be executed
584 sau khi nó đã đọc giá trị của
x = threading.Thread[target=thread_function, args=[1,], daemon=True]
29 nhưng trước khi nó ghi lại giá trị đã tăng

Các chi tiết về cách điều này xảy ra khá thú vị, nhưng không cần thiết cho phần còn lại của bài viết này, vì vậy vui lòng bỏ qua phần ẩn này

Điều này thực sự hoạt động như thế nàoHiển thị/Ẩn

Đoạn mã trên không hoàn toàn có sẵn như ban đầu bạn có thể nghĩ. Nó được thiết kế để bắt buộc một điều kiện cuộc đua mỗi khi bạn chạy nó, nhưng điều đó giúp giải quyết vấn đề dễ dàng hơn nhiều so với hầu hết các điều kiện cuộc đua

Có hai điều cần lưu ý khi nghĩ về điều kiện cuộc đua

  1. Ngay cả một hoạt động như

    import threading
    
    def task[]:
        print["timer object task running..."]
    
    if __name__=='__main__':
        t = threading.Timer[10, task]
        t.start[] # after 10 seconds, task will be executed
    
    586 cũng cần bộ xử lý nhiều bước. Mỗi bước này là một hướng dẫn riêng cho bộ xử lý

  2. Hệ điều hành có thể hoán đổi luồng nào đang chạy bất cứ lúc nào. Một chuỗi có thể được hoán đổi sau bất kỳ hướng dẫn nhỏ nào sau đây. Điều này có nghĩa là một luồng có thể được đặt ở chế độ ngủ để cho một luồng khác chạy ở giữa câu lệnh Python

Hãy xem xét điều này một cách chi tiết. REPL bên dưới hiển thị một hàm nhận một tham số và tăng nó

>>>

import threading

def task[]:
    print["timer object task running..."]

if __name__=='__main__':
    t = threading.Timer[10, task]
    t.start[] # after 10 seconds, task will be executed
54

Ví dụ REPL sử dụng

import threading

def task[]:
    print["timer object task running..."]

if __name__=='__main__':
    t = threading.Timer[10, task]
    t.start[] # after 10 seconds, task will be executed
587 từ thư viện chuẩn Python để hiển thị các bước nhỏ hơn mà bộ xử lý thực hiện để thực hiện chức năng của bạn. Nó thực hiện một
import threading

def task[]:
    print["timer object task running..."]

if __name__=='__main__':
    t = threading.Timer[10, task]
    t.start[] # after 10 seconds, task will be executed
588 của giá trị dữ liệu
x = threading.Thread[target=thread_function, args=[1,], daemon=True]
29, nó thực hiện một
import threading

def task[]:
    print["timer object task running..."]

if __name__=='__main__':
    t = threading.Timer[10, task]
    t.start[] # after 10 seconds, task will be executed
590 và sau đó nó sử dụng
import threading

def task[]:
    print["timer object task running..."]

if __name__=='__main__':
    t = threading.Timer[10, task]
    t.start[] # after 10 seconds, task will be executed
591 để cộng các giá trị đó lại với nhau

Chúng tôi dừng lại ở đây vì một lý do cụ thể. Đây là điểm trong

import threading

def task[]:
    print["timer object task running..."]

if __name__=='__main__':
    t = threading.Timer[10, task]
    t.start[] # after 10 seconds, task will be executed
509 ở trên nơi mà
import threading

def task[]:
    print["timer object task running..."]

if __name__=='__main__':
    t = threading.Timer[10, task]
    t.start[] # after 10 seconds, task will be executed
93 đã buộc các chủ đề chuyển đổi. Hoàn toàn có khả năng, thỉnh thoảng, hệ điều hành sẽ chuyển các luồng tại điểm chính xác đó ngay cả khi không có
import threading

def task[]:
    print["timer object task running..."]

if __name__=='__main__':
    t = threading.Timer[10, task]
    t.start[] # after 10 seconds, task will be executed
594, nhưng lệnh gọi tới
import threading

def task[]:
    print["timer object task running..."]

if __name__=='__main__':
    t = threading.Timer[10, task]
    t.start[] # after 10 seconds, task will be executed
594 khiến điều đó xảy ra mọi lúc

Như bạn đã tìm hiểu ở trên, hệ điều hành có thể hoán đổi luồng bất kỳ lúc nào. Bạn đã đi xuống danh sách này đến tuyên bố được đánh dấu

import threading

def task[]:
    print["timer object task running..."]

if __name__=='__main__':
    t = threading.Timer[10, task]
    t.start[] # after 10 seconds, task will be executed
596. Nếu hệ điều hành hoán đổi luồng này và chạy một luồng khác cũng sửa đổi
x = threading.Thread[target=thread_function, args=[1,], daemon=True]
29, thì khi luồng này hoạt động trở lại, nó sẽ ghi đè lên
x = threading.Thread[target=thread_function, args=[1,], daemon=True]
29 với một giá trị không chính xác

Về mặt kỹ thuật, ví dụ này sẽ không có điều kiện chạy đua vì

x = threading.Thread[target=thread_function, args=[1,], daemon=True]
29 là cục bộ của
x = threading.Thread[target=thread_function, args=[1,], daemon=True]
300. Tuy nhiên, nó minh họa cách một luồng có thể bị gián đoạn trong một thao tác Python. Nhóm hoạt động TẢI, SỬA ĐỔI, CỬA HÀNG tương tự cũng xảy ra trên các giá trị chung và giá trị chung. Bạn có thể khám phá với mô-đun
import threading

def task[]:
    print["timer object task running..."]

if __name__=='__main__':
    t = threading.Timer[10, task]
    t.start[] # after 10 seconds, task will be executed
587 và tự mình chứng minh điều đó

Rất hiếm khi xảy ra tình trạng cuộc đua như thế này, nhưng hãy nhớ rằng một sự kiện không thường xuyên xảy ra sau hàng triệu lần lặp lại sẽ có khả năng xảy ra. Sự hiếm có của các điều kiện chủng tộc này khiến chúng khó gỡ lỗi hơn nhiều so với các lỗi thông thường.

Bây giờ hãy quay lại phần hướng dẫn được lên lịch thường xuyên của bạn

Bây giờ bạn đã thấy một điều kiện chủng tộc đang hoạt động, hãy cùng tìm hiểu cách giải quyết chúng

Đồng bộ hóa cơ bản bằng cách sử dụng
x = threading.Thread[target=thread_function, args=[1,], daemon=True]
302

Có một số cách để tránh hoặc giải quyết các điều kiện chủng tộc. Bạn sẽ không xem xét tất cả chúng ở đây, nhưng có một số được sử dụng thường xuyên. Hãy bắt đầu với

x = threading.Thread[target=thread_function, args=[1,], daemon=True]
302

Để giải quyết tình trạng tương tranh của bạn ở trên, bạn cần tìm cách chỉ cho phép một chuỗi tại một thời điểm vào phần đọc-sửa-ghi trong mã của bạn. Cách phổ biến nhất để làm điều này được gọi là

x = threading.Thread[target=thread_function, args=[1,], daemon=True]
302 trong Python. Trong một số ngôn ngữ khác, ý tưởng tương tự này được gọi là
x = threading.Thread[target=thread_function, args=[1,], daemon=True]
305. Mutex đến từ Loại trừ MUTual, đó chính xác là những gì mà
x = threading.Thread[target=thread_function, args=[1,], daemon=True]
302 làm

Một

x = threading.Thread[target=thread_function, args=[1,], daemon=True]
302 là một đối tượng hoạt động giống như thẻ hành lang. Chỉ một chủ đề tại một thời điểm có thể có
x = threading.Thread[target=thread_function, args=[1,], daemon=True]
302. Bất kỳ chủ đề nào khác muốn có
x = threading.Thread[target=thread_function, args=[1,], daemon=True]
302 phải đợi cho đến khi chủ sở hữu của
x = threading.Thread[target=thread_function, args=[1,], daemon=True]
302 từ bỏ nó

Các chức năng cơ bản để làm điều này là

x = threading.Thread[target=thread_function, args=[1,], daemon=True]
311 và
x = threading.Thread[target=thread_function, args=[1,], daemon=True]
312. Một chủ đề sẽ gọi
x = threading.Thread[target=thread_function, args=[1,], daemon=True]
313 để lấy khóa. Nếu khóa đã được giữ, chuỗi cuộc gọi sẽ đợi cho đến khi nó được giải phóng. Có một điểm quan trọng ở đây. Nếu một luồng nhận được khóa nhưng không bao giờ trả lại, chương trình của bạn sẽ bị kẹt. Bạn sẽ đọc thêm về điều này sau

May mắn thay,

x = threading.Thread[target=thread_function, args=[1,], daemon=True]
302 của Python cũng sẽ hoạt động như một trình quản lý bối cảnh, vì vậy bạn có thể sử dụng nó trong câu lệnh
x = threading.Thread[target=thread_function, args=[1,], daemon=True]
40 và nó sẽ tự động được giải phóng khi khối
x = threading.Thread[target=thread_function, args=[1,], daemon=True]
40 thoát ra vì bất kỳ lý do gì

Hãy nhìn vào

import threading

def task[]:
    print["timer object task running..."]

if __name__=='__main__':
    t = threading.Timer[10, task]
    t.start[] # after 10 seconds, task will be executed
507 với một
x = threading.Thread[target=thread_function, args=[1,], daemon=True]
302 được thêm vào nó. Chức năng gọi vẫn giữ nguyên

import threading

def task[]:
    print["timer object task running..."]

if __name__=='__main__':
    t = threading.Timer[10, task]
    t.start[] # after 10 seconds, task will be executed
55

Ngoài việc thêm một loạt nhật ký gỡ lỗi để bạn có thể thấy khóa rõ ràng hơn, thay đổi lớn ở đây là thêm một thành viên có tên là

x = threading.Thread[target=thread_function, args=[1,], daemon=True]
319, là một đối tượng
x = threading.Thread[target=thread_function, args=[1,], daemon=True]
320.
x = threading.Thread[target=thread_function, args=[1,], daemon=True]
319 này được khởi tạo ở trạng thái không khóa và bị khóa và giải phóng bởi câu lệnh
x = threading.Thread[target=thread_function, args=[1,], daemon=True]
40

Điều đáng chú ý ở đây là chuỗi chạy chức năng này sẽ giữ lại

x = threading.Thread[target=thread_function, args=[1,], daemon=True]
302 đó cho đến khi hoàn tất việc cập nhật cơ sở dữ liệu. Trong trường hợp này, điều đó có nghĩa là nó sẽ giữ
x = threading.Thread[target=thread_function, args=[1,], daemon=True]
302 trong khi sao chép, cập nhật, ngủ và sau đó ghi giá trị trở lại cơ sở dữ liệu

Nếu bạn chạy phiên bản này với ghi nhật ký được đặt ở mức cảnh báo, bạn sẽ thấy điều này

import threading

def task[]:
    print["timer object task running..."]

if __name__=='__main__':
    t = threading.Timer[10, task]
    t.start[] # after 10 seconds, task will be executed
56

Nhìn kìa. Chương trình của bạn cuối cùng cũng hoạt động

Bạn có thể bật ghi nhật ký đầy đủ bằng cách đặt mức thành

x = threading.Thread[target=thread_function, args=[1,], daemon=True]
325 bằng cách thêm câu lệnh này sau khi bạn định cấu hình đầu ra ghi nhật ký trong
x = threading.Thread[target=thread_function, args=[1,], daemon=True]
14

import threading

def task[]:
    print["timer object task running..."]

if __name__=='__main__':
    t = threading.Timer[10, task]
    t.start[] # after 10 seconds, task will be executed
57

Chạy chương trình này với tính năng ghi nhật ký

x = threading.Thread[target=thread_function, args=[1,], daemon=True]
325 được bật trông như thế này

import threading

def task[]:
    print["timer object task running..."]

if __name__=='__main__':
    t = threading.Timer[10, task]
    t.start[] # after 10 seconds, task will be executed
58

Trong kết quả này, bạn có thể thấy

import threading

def task[]:
    print["timer object task running..."]

if __name__=='__main__':
    t = threading.Timer[10, task]
    t.start[] # after 10 seconds, task will be executed
505 nhận được khóa và vẫn đang giữ nó khi chuyển sang chế độ ngủ.
import threading

def task[]:
    print["timer object task running..."]

if __name__=='__main__':
    t = threading.Timer[10, task]
    t.start[] # after 10 seconds, task will be executed
504 sau đó bắt đầu và cố gắng lấy cùng một khóa. Vì
import threading

def task[]:
    print["timer object task running..."]

if __name__=='__main__':
    t = threading.Timer[10, task]
    t.start[] # after 10 seconds, task will be executed
505 vẫn đang giữ nên
import threading

def task[]:
    print["timer object task running..."]

if __name__=='__main__':
    t = threading.Timer[10, task]
    t.start[] # after 10 seconds, task will be executed
504 phải đợi. Đây là loại trừ lẫn nhau mà một
x = threading.Thread[target=thread_function, args=[1,], daemon=True]
302 cung cấp

Nhiều ví dụ trong phần còn lại của bài viết này sẽ có ghi nhật ký cấp độ

x = threading.Thread[target=thread_function, args=[1,], daemon=True]
333 và
x = threading.Thread[target=thread_function, args=[1,], daemon=True]
325. Nói chung, chúng tôi sẽ chỉ hiển thị đầu ra ở cấp độ
x = threading.Thread[target=thread_function, args=[1,], daemon=True]
333, vì nhật ký của
x = threading.Thread[target=thread_function, args=[1,], daemon=True]
325 có thể khá dài. Hãy thử các chương trình có đăng nhập và xem những gì họ làm

Loại bỏ các quảng cáo

Bế tắc

Trước khi tiếp tục, bạn nên xem xét một vấn đề thường gặp khi sử dụng

x = threading.Thread[target=thread_function, args=[1,], daemon=True]
337. Như bạn đã thấy, nếu
x = threading.Thread[target=thread_function, args=[1,], daemon=True]
302 đã được mua, lệnh gọi thứ hai đến
x = threading.Thread[target=thread_function, args=[1,], daemon=True]
311 sẽ đợi cho đến khi luồng đang giữ
x = threading.Thread[target=thread_function, args=[1,], daemon=True]
302 gọi
x = threading.Thread[target=thread_function, args=[1,], daemon=True]
312. Bạn nghĩ điều gì sẽ xảy ra khi bạn chạy mã này

import threading

def task[]:
    print["timer object task running..."]

if __name__=='__main__':
    t = threading.Timer[10, task]
    t.start[] # after 10 seconds, task will be executed
59

Khi chương trình gọi

x = threading.Thread[target=thread_function, args=[1,], daemon=True]
342 lần thứ hai, nó bị treo chờ
x = threading.Thread[target=thread_function, args=[1,], daemon=True]
302 được giải phóng. Trong ví dụ này, bạn có thể khắc phục bế tắc bằng cách loại bỏ cuộc gọi thứ hai, nhưng bế tắc thường xảy ra từ một trong hai điều nhỏ

  1. Lỗi triển khai trong đó
    x = threading.Thread[target=thread_function, args=[1,], daemon=True]
    
    302 không được phát hành đúng cách
  2. Một vấn đề thiết kế trong đó một chức năng tiện ích cần được gọi bởi các chức năng có thể có hoặc chưa có
    x = threading.Thread[target=thread_function, args=[1,], daemon=True]
    
    302

Tình huống đầu tiên đôi khi xảy ra, nhưng việc sử dụng

x = threading.Thread[target=thread_function, args=[1,], daemon=True]
302 làm trình quản lý bối cảnh sẽ giảm đáng kể tần suất. Bạn nên viết mã bất cứ khi nào có thể để sử dụng trình quản lý ngữ cảnh, vì chúng giúp tránh các tình huống mà một ngoại lệ bỏ qua bạn qua lệnh gọi
x = threading.Thread[target=thread_function, args=[1,], daemon=True]
312

Vấn đề thiết kế có thể phức tạp hơn một chút ở một số ngôn ngữ. Rất may, luồng Python có một đối tượng thứ hai, được gọi là

x = threading.Thread[target=thread_function, args=[1,], daemon=True]
348, được thiết kế cho tình huống này. Nó cho phép một thread tới
x = threading.Thread[target=thread_function, args=[1,], daemon=True]
311 và
x = threading.Thread[target=thread_function, args=[1,], daemon=True]
348 nhiều lần trước khi nó gọi
x = threading.Thread[target=thread_function, args=[1,], daemon=True]
312. Chủ đề đó vẫn được yêu cầu gọi
x = threading.Thread[target=thread_function, args=[1,], daemon=True]
312 bằng số lần nó gọi là
x = threading.Thread[target=thread_function, args=[1,], daemon=True]
311, nhưng dù sao thì nó cũng phải làm như vậy

x = threading.Thread[target=thread_function, args=[1,], daemon=True]
302 và
x = threading.Thread[target=thread_function, args=[1,], daemon=True]
348 là hai trong số các công cụ cơ bản được sử dụng trong lập trình luồng để ngăn chặn điều kiện chủng tộc. Có một số khác hoạt động theo những cách khác nhau. Trước khi bạn xem xét chúng, hãy chuyển sang một lĩnh vực vấn đề hơi khác

Luồng sản xuất-người tiêu dùng

Vấn đề Nhà sản xuất-Người tiêu dùng là một vấn đề khoa học máy tính tiêu chuẩn được sử dụng để xem xét các vấn đề về luồng hoặc quá trình đồng bộ hóa. Bạn sẽ xem xét một biến thể của nó để có một số ý tưởng về những gì nguyên thủy mà mô-đun Python

x = threading.Thread[target=thread_function, args=[1,], daemon=True]
6 cung cấp

Trong ví dụ này, bạn sẽ tưởng tượng một chương trình cần đọc tin nhắn từ mạng và ghi chúng vào đĩa. Chương trình không yêu cầu một tin nhắn khi nó muốn. Nó phải đang lắng nghe và chấp nhận tin nhắn khi chúng đến. Các tin nhắn sẽ không đến với tốc độ đều đặn mà sẽ đến từng đợt. Phần này của chương trình được gọi là nhà sản xuất

Mặt khác, khi bạn có một tin nhắn, bạn cần ghi nó vào cơ sở dữ liệu. Truy cập cơ sở dữ liệu chậm, nhưng đủ nhanh để theo kịp tốc độ trung bình của tin nhắn. Nó không đủ nhanh để theo kịp khi một loạt tin nhắn đến. Bộ phận này là người tiêu dùng

Ở giữa nhà sản xuất và người tiêu dùng, bạn sẽ tạo một

x = threading.Thread[target=thread_function, args=[1,], daemon=True]
357 sẽ là phần thay đổi khi bạn tìm hiểu về các đối tượng đồng bộ hóa khác nhau

Đó là bố cục cơ bản. Hãy xem xét một giải pháp bằng cách sử dụng

x = threading.Thread[target=thread_function, args=[1,], daemon=True]
302. Nó không hoạt động hoàn hảo, nhưng nó sử dụng các công cụ mà bạn đã biết, vì vậy đây là một nơi tốt để bắt đầu

Nhà sản xuất-Người tiêu dùng Sử dụng
x = threading.Thread[target=thread_function, args=[1,], daemon=True]
302

Vì đây là một bài viết về Python

x = threading.Thread[target=thread_function, args=[1,], daemon=True]
6 và vì bạn vừa đọc về nguyên hàm của
x = threading.Thread[target=thread_function, args=[1,], daemon=True]
302, hãy thử giải quyết vấn đề này với hai luồng bằng cách sử dụng một hoặc hai
x = threading.Thread[target=thread_function, args=[1,], daemon=True]
302.

Thiết kế chung là có một luồng

x = threading.Thread[target=thread_function, args=[1,], daemon=True]
363 đọc từ mạng giả mạo và đặt thông báo vào một
x = threading.Thread[target=thread_function, args=[1,], daemon=True]
357

x = threading.Thread[target=thread_function, args=[1,], daemon=True]
30

Để tạo một tin nhắn giả mạo,

x = threading.Thread[target=thread_function, args=[1,], daemon=True]
363 nhận một số ngẫu nhiên từ một đến một trăm. Nó gọi ________ 2366 trên ________ 2367 để gửi nó đến ________ 2368

x = threading.Thread[target=thread_function, args=[1,], daemon=True]
363 cũng sử dụng giá trị
x = threading.Thread[target=thread_function, args=[1,], daemon=True]
370 để báo hiệu cho người tiêu dùng dừng lại sau khi đã gửi mười giá trị. Điều này hơi khó xử, nhưng đừng lo, bạn sẽ thấy các cách để loại bỏ giá trị
x = threading.Thread[target=thread_function, args=[1,], daemon=True]
370 này sau khi bạn làm việc qua ví dụ này

Ở phía bên kia của

x = threading.Thread[target=thread_function, args=[1,], daemon=True]
367 là người tiêu dùng

x = threading.Thread[target=thread_function, args=[1,], daemon=True]
31

x = threading.Thread[target=thread_function, args=[1,], daemon=True]
368 đọc một tin nhắn từ
x = threading.Thread[target=thread_function, args=[1,], daemon=True]
367 và ghi nó vào một cơ sở dữ liệu giả mạo, trong trường hợp này chỉ là in nó ra màn hình. Nếu nó nhận được giá trị
x = threading.Thread[target=thread_function, args=[1,], daemon=True]
370, nó sẽ trả về từ hàm, điều này sẽ kết thúc luồng

Trước khi bạn xem phần thực sự thú vị, phần

x = threading.Thread[target=thread_function, args=[1,], daemon=True]
357, đây là phần
x = threading.Thread[target=thread_function, args=[1,], daemon=True]
14, tạo ra các chủ đề này

x = threading.Thread[target=thread_function, args=[1,], daemon=True]
32

Điều này trông khá quen thuộc vì nó gần với mã

x = threading.Thread[target=thread_function, args=[1,], daemon=True]
14 trong các ví dụ trước

Hãy nhớ rằng bạn có thể bật ghi nhật ký

x = threading.Thread[target=thread_function, args=[1,], daemon=True]
325 để xem tất cả các thông báo ghi nhật ký bằng cách bỏ ghi chú dòng này

x = threading.Thread[target=thread_function, args=[1,], daemon=True]
33

Có thể đáng để xem qua các thông báo ghi nhật ký của

x = threading.Thread[target=thread_function, args=[1,], daemon=True]
325 để xem chính xác vị trí mỗi luồng thu thập và giải phóng các khóa

Bây giờ chúng ta hãy xem

x = threading.Thread[target=thread_function, args=[1,], daemon=True]
357 truyền tin nhắn từ
x = threading.Thread[target=thread_function, args=[1,], daemon=True]
363 đến
x = threading.Thread[target=thread_function, args=[1,], daemon=True]
368

x = threading.Thread[target=thread_function, args=[1,], daemon=True]
34

ồ. Đó là rất nhiều mã. Một tỷ lệ phần trăm khá cao trong số đó chỉ là ghi lại các câu lệnh để dễ dàng xem điều gì đang xảy ra khi bạn chạy nó. Đây là cùng một mã với tất cả các câu lệnh ghi nhật ký đã bị xóa

x = threading.Thread[target=thread_function, args=[1,], daemon=True]
35

Điều đó có vẻ dễ quản lý hơn một chút.

x = threading.Thread[target=thread_function, args=[1,], daemon=True]
357 trong phiên bản mã này của bạn có ba thành viên

  1. x = threading.Thread[target=thread_function, args=[1,], daemon=True]
    
    385 lưu trữ tin nhắn để vượt qua
  2. x = threading.Thread[target=thread_function, args=[1,], daemon=True]
    
    386 là một đối tượng
    x = threading.Thread[target=thread_function, args=[1,], daemon=True]
    
    387 hạn chế quyền truy cập vào tin nhắn theo luồng
    x = threading.Thread[target=thread_function, args=[1,], daemon=True]
    
    363
  3. x = threading.Thread[target=thread_function, args=[1,], daemon=True]
    
    389 cũng là một
    x = threading.Thread[target=thread_function, args=[1,], daemon=True]
    
    387 hạn chế quyền truy cập vào thư theo chuỗi
    x = threading.Thread[target=thread_function, args=[1,], daemon=True]
    
    368

x = threading.Thread[target=thread_function, args=[1,], daemon=True]
392 khởi tạo ba thành viên này và sau đó gọi
x = threading.Thread[target=thread_function, args=[1,], daemon=True]
311 trên
x = threading.Thread[target=thread_function, args=[1,], daemon=True]
389. Đây là trạng thái bạn muốn bắt đầu.
x = threading.Thread[target=thread_function, args=[1,], daemon=True]
363 được phép thêm tin nhắn mới, nhưng
x = threading.Thread[target=thread_function, args=[1,], daemon=True]
368 cần đợi cho đến khi có tin nhắn

x = threading.Thread[target=thread_function, args=[1,], daemon=True]
397 và
x = threading.Thread[target=thread_function, args=[1,], daemon=True]
398 gần như đối lập nhau.
x = threading.Thread[target=thread_function, args=[1,], daemon=True]
397 cuộc gọi
x = threading.Thread[target=thread_function, args=[1,], daemon=True]
311 trên
x = threading.Thread[target=thread_function, args=[1,], daemon=True]
01. Đây là cuộc gọi sẽ khiến
x = threading.Thread[target=thread_function, args=[1,], daemon=True]
368 đợi cho đến khi có tin nhắn

Khi

x = threading.Thread[target=thread_function, args=[1,], daemon=True]
368 đã có được
x = threading.Thread[target=thread_function, args=[1,], daemon=True]
389, nó sẽ sao chép giá trị trong
x = threading.Thread[target=thread_function, args=[1,], daemon=True]
385 và sau đó gọi
x = threading.Thread[target=thread_function, args=[1,], daemon=True]
312 trên
x = threading.Thread[target=thread_function, args=[1,], daemon=True]
386. Mở khóa này là điều cho phép ________ 2363 chèn thông báo tiếp theo vào ________ 2367

Trước khi bạn tiếp tục với

x = threading.Thread[target=thread_function, args=[1,], daemon=True]
366, có một điều gì đó tế nhị đang diễn ra trong
x = threading.Thread[target=thread_function, args=[1,], daemon=True]
397 mà bạn rất dễ bỏ sót. Nó có vẻ hấp dẫn để loại bỏ
x = threading.Thread[target=thread_function, args=[1,], daemon=True]
12 và chỉ để hàm kết thúc bằng
x = threading.Thread[target=thread_function, args=[1,], daemon=True]
13. Xem liệu bạn có thể tìm ra lý do tại sao bạn không muốn làm điều đó trước khi tiếp tục

Đây là câu trả lời. Ngay khi

x = threading.Thread[target=thread_function, args=[1,], daemon=True]
368 gọi
x = threading.Thread[target=thread_function, args=[1,], daemon=True]
15, nó có thể được hoán đổi và
x = threading.Thread[target=thread_function, args=[1,], daemon=True]
363 có thể bắt đầu chạy. Điều đó có thể xảy ra trước khi
x = threading.Thread[target=thread_function, args=[1,], daemon=True]
312 trở lại. Điều này có nghĩa là có một khả năng nhỏ là khi hàm trả về
x = threading.Thread[target=thread_function, args=[1,], daemon=True]
18, đó thực sự có thể là thông báo tiếp theo được tạo, vì vậy bạn sẽ mất thông báo đầu tiên. Đây là một ví dụ khác về điều kiện chủng tộc

Chuyển sang

x = threading.Thread[target=thread_function, args=[1,], daemon=True]
366, bạn có thể thấy mặt trái của giao dịch.
x = threading.Thread[target=thread_function, args=[1,], daemon=True]
363 sẽ gọi điều này bằng một tin nhắn. Nó sẽ nhận được
x = threading.Thread[target=thread_function, args=[1,], daemon=True]
386, đặt
x = threading.Thread[target=thread_function, args=[1,], daemon=True]
385 và gọi
x = threading.Thread[target=thread_function, args=[1,], daemon=True]
312 sau đó là
x = threading.Thread[target=thread_function, args=[1,], daemon=True]
01, điều này sẽ cho phép
x = threading.Thread[target=thread_function, args=[1,], daemon=True]
368 đọc giá trị đó

Hãy chạy mã có ghi nhật ký được đặt thành

x = threading.Thread[target=thread_function, args=[1,], daemon=True]
333 và xem nó trông như thế nào

x = threading.Thread[target=thread_function, args=[1,], daemon=True]
36

Lúc đầu, bạn có thể thấy lạ khi nhà sản xuất nhận được hai tin nhắn trước khi người tiêu dùng thậm chí chạy. Nếu bạn nhìn lại

x = threading.Thread[target=thread_function, args=[1,], daemon=True]
363 và
x = threading.Thread[target=thread_function, args=[1,], daemon=True]
366, bạn sẽ nhận thấy rằng nơi duy nhất mà nó chờ đợi một
x = threading.Thread[target=thread_function, args=[1,], daemon=True]
302 là khi nó cố gắng đưa thông báo vào đường dẫn. Điều này được thực hiện sau khi
x = threading.Thread[target=thread_function, args=[1,], daemon=True]
363 nhận được thông báo và ghi lại rằng nó đã nhận được thông báo đó

Khi

x = threading.Thread[target=thread_function, args=[1,], daemon=True]
363 cố gắng gửi tin nhắn thứ hai này, nó sẽ gọi
x = threading.Thread[target=thread_function, args=[1,], daemon=True]
366 lần thứ hai và nó sẽ chặn

Hệ điều hành có thể hoán đổi các luồng bất cứ lúc nào, nhưng nó thường cho phép mỗi luồng có một khoảng thời gian hợp lý để chạy trước khi hoán đổi nó. Đó là lý do tại sao

x = threading.Thread[target=thread_function, args=[1,], daemon=True]
363 thường chạy cho đến khi nó chặn lệnh gọi thứ hai tới
x = threading.Thread[target=thread_function, args=[1,], daemon=True]
366

Tuy nhiên, khi một luồng bị chặn, hệ điều hành sẽ luôn hoán đổi nó và tìm một luồng khác để chạy. Trong trường hợp này, chuỗi duy nhất khác có bất kỳ việc gì phải làm là

x = threading.Thread[target=thread_function, args=[1,], daemon=True]
368

x = threading.Thread[target=thread_function, args=[1,], daemon=True]
368 gọi
x = threading.Thread[target=thread_function, args=[1,], daemon=True]
397, đọc tin nhắn và gọi
x = threading.Thread[target=thread_function, args=[1,], daemon=True]
312 trên
x = threading.Thread[target=thread_function, args=[1,], daemon=True]
386, do đó cho phép
x = threading.Thread[target=thread_function, args=[1,], daemon=True]
363 chạy lại vào lần tiếp theo các chuỗi được hoán đổi

Lưu ý rằng tin nhắn đầu tiên là

x = threading.Thread[target=thread_function, args=[1,], daemon=True]
41 và đó chính xác là những gì mà
x = threading.Thread[target=thread_function, args=[1,], daemon=True]
368 đã đọc, mặc dù
x = threading.Thread[target=thread_function, args=[1,], daemon=True]
363 đã tạo ra tin nhắn
x = threading.Thread[target=thread_function, args=[1,], daemon=True]
44

Mặc dù nó hoạt động cho thử nghiệm hạn chế này, nhưng nó không phải là một giải pháp tuyệt vời cho vấn đề người sản xuất-người tiêu dùng nói chung vì nó chỉ cho phép một giá trị duy nhất trong quy trình tại một thời điểm. Khi

x = threading.Thread[target=thread_function, args=[1,], daemon=True]
363 nhận được một loạt tin nhắn, nó sẽ không có nơi nào để đặt chúng

Hãy chuyển sang một cách tốt hơn để giải quyết vấn đề này, sử dụng

x = threading.Thread[target=thread_function, args=[1,], daemon=True]
46

Loại bỏ các quảng cáo

Nhà sản xuất-Người tiêu dùng Sử dụng
x = threading.Thread[target=thread_function, args=[1,], daemon=True]
46

Nếu bạn muốn có thể xử lý nhiều giá trị trong quy trình cùng một lúc, bạn sẽ cần cấu trúc dữ liệu cho quy trình cho phép số lượng tăng và giảm khi dữ liệu được sao lưu từ

x = threading.Thread[target=thread_function, args=[1,], daemon=True]
363

Thư viện chuẩn của Python có một mô-đun

x = threading.Thread[target=thread_function, args=[1,], daemon=True]
49, đến lượt nó, có một lớp
x = threading.Thread[target=thread_function, args=[1,], daemon=True]
46. Hãy thay đổi
x = threading.Thread[target=thread_function, args=[1,], daemon=True]
357 để sử dụng một
x = threading.Thread[target=thread_function, args=[1,], daemon=True]
46 thay vì chỉ một biến được bảo vệ bởi một
x = threading.Thread[target=thread_function, args=[1,], daemon=True]
302. Bạn cũng sẽ sử dụng một cách khác để dừng chuỗi công nhân bằng cách sử dụng một nguyên hàm khác từ Python
x = threading.Thread[target=thread_function, args=[1,], daemon=True]
6, một
x = threading.Thread[target=thread_function, args=[1,], daemon=True]
55

Hãy bắt đầu với

x = threading.Thread[target=thread_function, args=[1,], daemon=True]
55. Đối tượng
x = threading.Thread[target=thread_function, args=[1,], daemon=True]
57 cho phép một luồng báo hiệu một
x = threading.Thread[target=thread_function, args=[1,], daemon=True]
58 trong khi nhiều luồng khác có thể chờ đợi
x = threading.Thread[target=thread_function, args=[1,], daemon=True]
58 đó xảy ra. Cách sử dụng chính trong mã này là các luồng đang chờ sự kiện không nhất thiết phải dừng việc chúng đang làm, chúng chỉ cần thỉnh thoảng kiểm tra trạng thái của
x = threading.Thread[target=thread_function, args=[1,], daemon=True]
55

Việc kích hoạt sự kiện có thể là nhiều thứ. Trong ví dụ này, luồng chính sẽ đơn giản là ngủ một lúc và sau đó

x = threading.Thread[target=thread_function, args=[1,], daemon=True]
61 nó

x = threading.Thread[target=thread_function, args=[1,], daemon=True]
37

Những thay đổi duy nhất ở đây là việc tạo đối tượng

x = threading.Thread[target=thread_function, args=[1,], daemon=True]
58 trên dòng 8, chuyển
x = threading.Thread[target=thread_function, args=[1,], daemon=True]
58 làm tham số trên dòng 10 và 11, và phần cuối cùng trên dòng 13 đến 15, ngủ trong một giây, ghi nhật ký và sau đó gọi

x = threading.Thread[target=thread_function, args=[1,], daemon=True]
363 cũng không phải thay đổi quá nhiều

x = threading.Thread[target=thread_function, args=[1,], daemon=True]
38

Bây giờ nó sẽ lặp lại cho đến khi thấy rằng sự kiện đã được đặt trên dòng 3. Nó cũng không còn đặt giá trị

x = threading.Thread[target=thread_function, args=[1,], daemon=True]
370 vào
x = threading.Thread[target=thread_function, args=[1,], daemon=True]
367

x = threading.Thread[target=thread_function, args=[1,], daemon=True]
368 phải thay đổi thêm một chút

x = threading.Thread[target=thread_function, args=[1,], daemon=True]
39

Trong khi bạn phải lấy mã liên quan đến giá trị ________ 2370, bạn phải thực hiện một điều kiện ________ 270 phức tạp hơn một chút. Nó không chỉ lặp cho đến khi

x = threading.Thread[target=thread_function, args=[1,], daemon=True]
58 được đặt, mà còn cần tiếp tục lặp cho đến khi
x = threading.Thread[target=thread_function, args=[1,], daemon=True]
367 được làm trống

Đảm bảo hàng đợi trống trước khi người tiêu dùng kết thúc sẽ ngăn chặn một vấn đề thú vị khác. Nếu

x = threading.Thread[target=thread_function, args=[1,], daemon=True]
368 thoát trong khi
x = threading.Thread[target=thread_function, args=[1,], daemon=True]
367 có tin nhắn trong đó, có hai điều tồi tệ có thể xảy ra. Đầu tiên là bạn mất những tin nhắn cuối cùng đó, nhưng điều nghiêm trọng hơn là
x = threading.Thread[target=thread_function, args=[1,], daemon=True]
363 có thể bị bắt khi cố gắng thêm một tin nhắn vào hàng đợi đầy đủ và không bao giờ quay lại

Điều này xảy ra nếu

x = threading.Thread[target=thread_function, args=[1,], daemon=True]
58 được kích hoạt sau khi
x = threading.Thread[target=thread_function, args=[1,], daemon=True]
363 đã kiểm tra điều kiện
x = threading.Thread[target=thread_function, args=[1,], daemon=True]
78 nhưng trước khi gọi
x = threading.Thread[target=thread_function, args=[1,], daemon=True]
79

Nếu điều đó xảy ra, người tiêu dùng có thể thức dậy và thoát ra với hàng đợi vẫn đầy đủ. Sau đó, ________ 2363 sẽ gọi ________ 2366 sẽ đợi cho đến khi có chỗ trống trên hàng đợi cho tin nhắn mới.

x = threading.Thread[target=thread_function, args=[1,], daemon=True]
368 đã thoát, vì vậy điều này sẽ không xảy ra và
x = threading.Thread[target=thread_function, args=[1,], daemon=True]
363 sẽ không thoát

Phần còn lại của

x = threading.Thread[target=thread_function, args=[1,], daemon=True]
368 sẽ trông quen thuộc

Tuy nhiên,

x = threading.Thread[target=thread_function, args=[1,], daemon=True]
357 đã thay đổi đáng kể

x = threading.Thread[target=thread_function, args=[1,], daemon=True]
0

Bạn có thể thấy rằng

x = threading.Thread[target=thread_function, args=[1,], daemon=True]
357 là một lớp con của
x = threading.Thread[target=thread_function, args=[1,], daemon=True]
87.
x = threading.Thread[target=thread_function, args=[1,], daemon=True]
46 có một tham số tùy chọn khi khởi tạo để chỉ định kích thước tối đa của hàng đợi

Nếu bạn đưa ra một số dương cho

x = threading.Thread[target=thread_function, args=[1,], daemon=True]
89, nó sẽ giới hạn hàng đợi ở số phần tử đó, khiến cho
x = threading.Thread[target=thread_function, args=[1,], daemon=True]
90 bị chặn cho đến khi có ít hơn
x = threading.Thread[target=thread_function, args=[1,], daemon=True]
89 phần tử. Nếu bạn không chỉ định
x = threading.Thread[target=thread_function, args=[1,], daemon=True]
89, thì hàng đợi sẽ phát triển đến giới hạn bộ nhớ máy tính của bạn

x = threading.Thread[target=thread_function, args=[1,], daemon=True]
397 và
x = threading.Thread[target=thread_function, args=[1,], daemon=True]
366 nhỏ hơn nhiều. Về cơ bản, chúng bọc
x = threading.Thread[target=thread_function, args=[1,], daemon=True]
95 và
x = threading.Thread[target=thread_function, args=[1,], daemon=True]
90 trên
x = threading.Thread[target=thread_function, args=[1,], daemon=True]
46. Bạn có thể tự hỏi tất cả các mã khóa ngăn chặn các chủ đề gây ra tình trạng cuộc đua đã đi đâu

Các nhà phát triển cốt lõi đã viết thư viện tiêu chuẩn biết rằng

x = threading.Thread[target=thread_function, args=[1,], daemon=True]
46 thường được sử dụng trong môi trường đa luồng và đã kết hợp tất cả mã khóa đó bên trong chính
x = threading.Thread[target=thread_function, args=[1,], daemon=True]
46.
x = threading.Thread[target=thread_function, args=[1,], daemon=True]
46 là chủ đề an toàn

Chạy chương trình này trông giống như sau

x = threading.Thread[target=thread_function, args=[1,], daemon=True]
1

Nếu bạn đọc qua đầu ra trong ví dụ của tôi, bạn có thể thấy một số điều thú vị đang xảy ra. Ngay ở trên cùng, bạn có thể thấy

x = threading.Thread[target=thread_function, args=[1,], daemon=True]
363 phải tạo năm tin nhắn và đặt bốn tin nhắn trong số đó vào hàng đợi. Nó đã bị hệ điều hành tráo đổi trước khi nó có thể đặt cái thứ năm

Sau đó,

x = threading.Thread[target=thread_function, args=[1,], daemon=True]
368 đã chạy và gửi tin nhắn đầu tiên. Nó in ra thông báo đó cũng như độ sâu của hàng đợi tại thời điểm đó

x = threading.Thread[target=thread_function, args=[1,], daemon=True]
2

Đây là cách bạn biết rằng tin nhắn thứ năm vẫn chưa được gửi đến ________ 2367. Hàng đợi giảm xuống kích thước ba sau khi một tin nhắn bị xóa. Bạn cũng biết rằng

x = threading.Thread[target=thread_function, args=[1,], daemon=True]
49 có thể chứa mười tin nhắn, vì vậy chuỗi
x = threading.Thread[target=thread_function, args=[1,], daemon=True]
363 không bị chặn bởi
x = threading.Thread[target=thread_function, args=[1,], daemon=True]
49. Nó đã bị tráo đổi bởi hệ điều hành

Ghi chú. Đầu ra của bạn sẽ khác. Đầu ra của bạn sẽ thay đổi từ lần chạy này sang lần chạy khác. Đó là phần thú vị khi làm việc với các chủ đề

Khi chương trình bắt đầu kết thúc, bạn có thể thấy luồng chính tạo ra

x = threading.Thread[target=thread_function, args=[1,], daemon=True]
58 khiến cho
x = threading.Thread[target=thread_function, args=[1,], daemon=True]
363 thoát ngay lập tức không.
x = threading.Thread[target=thread_function, args=[1,], daemon=True]
368 vẫn còn rất nhiều việc phải làm, vì vậy nó sẽ tiếp tục chạy cho đến khi dọn sạch
x = threading.Thread[target=thread_function, args=[1,], daemon=True]
367

Hãy thử chơi với các kích thước hàng đợi khác nhau và các cuộc gọi tới

import threading

def task[]:
    print["timer object task running..."]

if __name__=='__main__':
    t = threading.Timer[10, task]
    t.start[] # after 10 seconds, task will be executed
93 trong
x = threading.Thread[target=thread_function, args=[1,], daemon=True]
363 hoặc
x = threading.Thread[target=thread_function, args=[1,], daemon=True]
368 để mô phỏng thời gian truy cập mạng hoặc ổ đĩa lâu hơn tương ứng. Ngay cả những thay đổi nhỏ đối với các yếu tố này của chương trình cũng sẽ tạo ra sự khác biệt lớn trong kết quả của bạn

Đây là một giải pháp tốt hơn nhiều cho vấn đề người sản xuất-người tiêu dùng, nhưng bạn có thể đơn giản hóa nó hơn nữa.

x = threading.Thread[target=thread_function, args=[1,], daemon=True]
357 thực sự không cần thiết cho vấn đề này. Khi bạn xóa nhật ký, nó sẽ trở thành một
x = threading.Thread[target=thread_function, args=[1,], daemon=True]
87

Đây là mã cuối cùng trông như thế nào khi sử dụng trực tiếp

x = threading.Thread[target=thread_function, args=[1,], daemon=True]
87

x = threading.Thread[target=thread_function, args=[1,], daemon=True]
3

Điều đó dễ đọc hơn và cho thấy cách sử dụng các nguyên hàm tích hợp sẵn của Python có thể đơn giản hóa một vấn đề phức tạp

x = threading.Thread[target=thread_function, args=[1,], daemon=True]
302 và
x = threading.Thread[target=thread_function, args=[1,], daemon=True]
46 là các lớp tiện dụng để giải quyết các vấn đề tương tranh, nhưng có những lớp khác do thư viện chuẩn cung cấp. Trước khi bạn kết thúc hướng dẫn này, hãy thực hiện một cuộc khảo sát nhanh về một số trong số chúng

Loại bỏ các quảng cáo

đối tượng luồng

Có một vài nguyên mẫu khác được cung cấp bởi mô-đun Python

x = threading.Thread[target=thread_function, args=[1,], daemon=True]
6. Mặc dù bạn không cần những thứ này cho các ví dụ trên, nhưng chúng có thể hữu ích trong các trường hợp sử dụng khác nhau, vì vậy bạn nên làm quen với chúng

đèn hiệu

Đối tượng Python

x = threading.Thread[target=thread_function, args=[1,], daemon=True]
6 đầu tiên cần xem xét là
import threading

def task[]:
    print["timer object task running..."]

if __name__=='__main__':
    t = threading.Timer[10, task]
    t.start[] # after 10 seconds, task will be executed
021. Một
import threading

def task[]:
    print["timer object task running..."]

if __name__=='__main__':
    t = threading.Timer[10, task]
    t.start[] # after 10 seconds, task will be executed
022 là một bộ đếm có một vài thuộc tính đặc biệt. Đầu tiên là việc đếm là nguyên tử. Điều này có nghĩa là đảm bảo rằng hệ điều hành sẽ không tráo đổi luồng khi đang tăng hoặc giảm bộ đếm

Bộ đếm nội bộ được tăng lên khi bạn gọi

x = threading.Thread[target=thread_function, args=[1,], daemon=True]
312 và giảm đi khi bạn gọi
x = threading.Thread[target=thread_function, args=[1,], daemon=True]
311

Thuộc tính đặc biệt tiếp theo là nếu một luồng gọi

x = threading.Thread[target=thread_function, args=[1,], daemon=True]
311 khi bộ đếm bằng 0, luồng đó sẽ chặn cho đến khi một luồng khác gọi
x = threading.Thread[target=thread_function, args=[1,], daemon=True]
312 và tăng bộ đếm lên một

Semaphores thường được sử dụng để bảo vệ tài nguyên có dung lượng hạn chế. Một ví dụ sẽ là nếu bạn có một nhóm kết nối và muốn giới hạn kích thước của nhóm đó ở một số cụ thể

hẹn giờ

import threading

def task[]:
    print["timer object task running..."]

if __name__=='__main__':
    t = threading.Timer[10, task]
    t.start[] # after 10 seconds, task will be executed
027 là một cách để lên lịch một chức năng được gọi sau một khoảng thời gian nhất định đã trôi qua. Bạn tạo một
import threading

def task[]:
    print["timer object task running..."]

if __name__=='__main__':
    t = threading.Timer[10, task]
    t.start[] # after 10 seconds, task will be executed
028 bằng cách chuyển trong một số giây để đợi và một chức năng để gọi

x = threading.Thread[target=thread_function, args=[1,], daemon=True]
4

Bạn bắt đầu

import threading

def task[]:
    print["timer object task running..."]

if __name__=='__main__':
    t = threading.Timer[10, task]
    t.start[] # after 10 seconds, task will be executed
028 bằng cách gọi
import threading

def task[]:
    print["timer object task running..."]

if __name__=='__main__':
    t = threading.Timer[10, task]
    t.start[] # after 10 seconds, task will be executed
05. Hàm sẽ được gọi trên một luồng mới vào một thời điểm nào đó sau thời gian đã chỉ định, nhưng lưu ý rằng không có lời hứa nào rằng nó sẽ được gọi chính xác vào thời điểm bạn muốn

Nếu bạn muốn dừng một

import threading

def task[]:
    print["timer object task running..."]

if __name__=='__main__':
    t = threading.Timer[10, task]
    t.start[] # after 10 seconds, task will be executed
028 mà bạn đã bắt đầu, bạn có thể hủy nó bằng cách gọi cho
import threading

def task[]:
    print["timer object task running..."]

if __name__=='__main__':
    t = threading.Timer[10, task]
    t.start[] # after 10 seconds, task will be executed
032. Gọi
import threading

def task[]:
    print["timer object task running..."]

if __name__=='__main__':
    t = threading.Timer[10, task]
    t.start[] # after 10 seconds, task will be executed
032 sau khi
import threading

def task[]:
    print["timer object task running..."]

if __name__=='__main__':
    t = threading.Timer[10, task]
    t.start[] # after 10 seconds, task will be executed
028 đã kích hoạt không làm gì cả và không tạo ra ngoại lệ

Một

import threading

def task[]:
    print["timer object task running..."]

if __name__=='__main__':
    t = threading.Timer[10, task]
    t.start[] # after 10 seconds, task will be executed
028 có thể được sử dụng để nhắc người dùng hành động sau một khoảng thời gian cụ thể. Nếu người dùng thực hiện hành động trước khi
import threading

def task[]:
    print["timer object task running..."]

if __name__=='__main__':
    t = threading.Timer[10, task]
    t.start[] # after 10 seconds, task will be executed
028 hết hạn, thì có thể gọi
import threading

def task[]:
    print["timer object task running..."]

if __name__=='__main__':
    t = threading.Timer[10, task]
    t.start[] # after 10 seconds, task will be executed
032

Rào chắn

Một

import threading

def task[]:
    print["timer object task running..."]

if __name__=='__main__':
    t = threading.Timer[10, task]
    t.start[] # after 10 seconds, task will be executed
038 có thể được sử dụng để giữ đồng bộ một số luồng cố định. Khi tạo một
import threading

def task[]:
    print["timer object task running..."]

if __name__=='__main__':
    t = threading.Timer[10, task]
    t.start[] # after 10 seconds, task will be executed
039, người gọi phải chỉ định có bao nhiêu luồng sẽ được đồng bộ hóa trên đó. Mỗi luồng gọi
import threading

def task[]:
    print["timer object task running..."]

if __name__=='__main__':
    t = threading.Timer[10, task]
    t.start[] # after 10 seconds, task will be executed
040 trên
import threading

def task[]:
    print["timer object task running..."]

if __name__=='__main__':
    t = threading.Timer[10, task]
    t.start[] # after 10 seconds, task will be executed
039. Tất cả chúng sẽ vẫn bị chặn cho đến khi số lượng chủ đề được chỉ định đang chờ và sau đó tất cả sẽ được giải phóng cùng một lúc

Hãy nhớ rằng các luồng được lên lịch bởi hệ điều hành, vì vậy, mặc dù tất cả các luồng được giải phóng đồng thời, chúng sẽ được lên lịch để chạy từng luồng một

Một cách sử dụng cho

import threading

def task[]:
    print["timer object task running..."]

if __name__=='__main__':
    t = threading.Timer[10, task]
    t.start[] # after 10 seconds, task will be executed
039 là cho phép một nhóm các luồng tự khởi tạo. Để các luồng chờ trên
import threading

def task[]:
    print["timer object task running..."]

if __name__=='__main__':
    t = threading.Timer[10, task]
    t.start[] # after 10 seconds, task will be executed
039 sau khi chúng được khởi tạo sẽ đảm bảo rằng không có luồng nào bắt đầu chạy trước khi tất cả các luồng kết thúc quá trình khởi tạo của chúng

Sự kết luận. Luồng trong Python

Bây giờ bạn đã thấy phần lớn những gì Python

x = threading.Thread[target=thread_function, args=[1,], daemon=True]
6 cung cấp và một số ví dụ về cách xây dựng các chương trình theo luồng và các vấn đề mà chúng giải quyết. Bạn cũng đã thấy một vài ví dụ về các vấn đề phát sinh khi viết và gỡ lỗi các chương trình luồng.

Nếu bạn muốn khám phá các tùy chọn khác để đồng thời trong Python, hãy xem Tăng tốc chương trình Python của bạn với đồng thời

Nếu bạn muốn tìm hiểu sâu về mô-đun

x = threading.Thread[target=thread_function, args=[1,], daemon=True]
8, hãy đọc Async IO trong Python. Hướng dẫn hoàn chỉnh

Dù bạn làm gì, giờ đây bạn đã có thông tin và sự tự tin cần thiết để viết chương trình bằng luồng Python

Đặc biệt cảm ơn độc giả JL Diaz đã giúp làm sạch phần giới thiệu

Lấy bài kiểm tra. Kiểm tra kiến ​​thức của bạn với bài kiểm tra tương tác “Python Threading” của chúng tôi. Sau khi hoàn thành, bạn sẽ nhận được điểm số để có thể theo dõi quá trình học tập của mình theo thời gian

Lấy bài kiểm tra "

Đánh dấu là đã hoàn thành

Xem ngay Hướng dẫn này có một khóa học video liên quan do nhóm Real Python tạo. Xem nó cùng với hướng dẫn bằng văn bản để hiểu sâu hơn. Tạo luồng trong Python

🐍 Thủ thuật Python 💌

Nhận một Thủ thuật Python ngắn và hấp dẫn được gửi đến hộp thư đến của bạn vài ngày một lần. Không có thư rác bao giờ. Hủy đăng ký bất cứ lúc nào. Được quản lý bởi nhóm Real Python

Gửi cho tôi thủ thuật Python »

Về Jim Anderson

Jim đã lập trình trong một thời gian dài bằng nhiều ngôn ngữ. Anh ấy đã làm việc trên các hệ thống nhúng, xây dựng các hệ thống xây dựng phân tán, quản lý nhà cung cấp nước ngoài và tham gia rất nhiều cuộc họp

» Thông tin thêm về Jim

Mỗi hướng dẫn tại Real Python được tạo bởi một nhóm các nhà phát triển để nó đáp ứng các tiêu chuẩn chất lượng cao của chúng tôi. Các thành viên trong nhóm đã làm việc trong hướng dẫn này là

Aldren

Brad

Joanna

Bậc thầy Kỹ năng Python trong thế giới thực Với quyền truy cập không giới hạn vào Python thực

Tham gia với chúng tôi và có quyền truy cập vào hàng nghìn hướng dẫn, khóa học video thực hành và cộng đồng các Pythonistas chuyên gia

Nâng cao kỹ năng Python của bạn »

Bậc thầy Kỹ năng Python trong thế giới thực
Với quyền truy cập không giới hạn vào Python thực

Tham gia với chúng tôi và có quyền truy cập vào hàng ngàn hướng dẫn, khóa học video thực hành và cộng đồng Pythonistas chuyên gia

Nâng cao kỹ năng Python của bạn »

Bạn nghĩ sao?

Đánh giá bài viết này

Tweet Chia sẻ Chia sẻ Email

Bài học số 1 hoặc điều yêu thích mà bạn đã học được là gì?

Mẹo bình luận. Những nhận xét hữu ích nhất là những nhận xét được viết với mục đích học hỏi hoặc giúp đỡ các sinh viên khác. và nhận câu trả lời cho các câu hỏi phổ biến trong cổng thông tin hỗ trợ của chúng tôi

Bộ hẹn giờ phân luồng hệ thống hoạt động như thế nào?

Luồng ứng dụng tạo bộ đếm thời gian, đợi một giây rồi thực thi phương thức gọi lại CheckStatus sau mỗi 250 mili giây . Chuỗi ứng dụng sau đó chặn cho đến khi đối tượng AutoResetEvent được báo hiệu. Khi phương thức gọi lại CheckStatus thực thi maxCount lần, nó sẽ gọi AutoResetEvent.

Chủ đề hẹn giờ là gì?

Bộ hẹn giờ là một lớp con của Chủ đề . Lớp bộ hẹn giờ đại diện cho một hành động chỉ được chạy sau một khoảng thời gian nhất định đã trôi qua. Bộ hẹn giờ bắt đầu hoạt động sau một khoảng thời gian trễ và có thể bị hủy tại bất kỳ thời điểm nào trong khoảng thời gian trễ đó. Bộ hẹn giờ được bắt đầu, như với các luồng, bằng cách gọi phương thức start[] của chúng.

Chuỗi Python hoạt động như thế nào?

Đa luồng [đôi khi chỉ đơn giản là "phân luồng"] là khi một chương trình tạo nhiều luồng với chu kỳ thực thi giữa chúng, do đó, một tác vụ chạy lâu hơn không chặn tất cả các tác vụ khác. This works well for tasks that can be broken down into smaller subtasks, which can then each be given to a thread to be completed.

Chủ Đề