Nhập phân luồng Python

Các luồng Python được sử dụng trong trường hợp việc thực hiện một tác vụ liên quan đến việc chờ đợi. Một ví dụ sẽ là tương tác với một dịch vụ được lưu trữ trên một máy tính khác, chẳng hạn như máy chủ web. Luồng cho phép python thực thi mã khác trong khi chờ đợi;

Một ví dụ tối thiểu với lệnh gọi hàm[sửa | sửa mã nguồn]. sửa nguồn]

Tạo chuỗi in các số từ 1-10 và đợi một giây giữa mỗi lần in

import threading
import time

def loop1_10[]:
    for i in range[1, 11]:
        time.sleep[1]
        print[i]

threading.Thread[target=loop1_10].start[]

Một ví dụ tối thiểu với đối tượng[sửa | sửa mã nguồn]. sửa nguồn]

#!/usr/bin/env python

import threading
import time


class MyThread[threading.Thread]:
    def run[self]:                                         # Default called function with mythread.start[]
        print["{} started!".format[self.getName[]]]        # "Thread-x started!"
        time.sleep[1]                                      # Pretend to work for a second
        print["{} finished!".format[self.getName[]]]       # "Thread-x finished!"

def main[]:
    for x in range[4]:                                     # Four times...
        mythread = MyThread[name = "Thread-{}".format[x]]  # ...Instantiate a thread and pass a unique ID to it
        mythread.start[]                                   # ...Start the thread, run method will be invoked
        time.sleep[.9]                                     # ...Wait 0.9 seconds before starting another

if __name__ == '__main__':
    main[]

Đầu ra trông như thế này

Thread-0 started!
Thread-1 started!
Thread-0 finished!
Thread-2 started!
Thread-1 finished!
Thread-3 started!
Thread-2 finished!
Thread-3 finished!

Mô-đun luồng được xây dựng trên các tính năng cấp thấp của luồng để làm việc với các luồng thậm chí dễ dàng hơn và Pythonic hơn. Sử dụng các luồng cho phép một chương trình chạy đồng thời nhiều thao tác trong cùng một không gian xử lý

Đối tượng chủ đề

Cách đơn giản nhất để sử dụng một Chủ đề là khởi tạo nó bằng một hàm mục tiêu và gọi start[] để nó bắt đầu hoạt động

import threading

def worker[]:
    """thread worker function"""
    print 'Worker'
    return

threads = []
for i in range[5]:
    t = threading.Thread[target=worker]
    threads.append[t]
    t.start[]

Đầu ra là năm dòng với "Công nhân" trên mỗi dòng

$ python threading_simple.py

Worker
Worker
Worker
Worker
Worker

Thật hữu ích khi có thể sinh ra một luồng và truyền cho nó các đối số để cho nó biết công việc cần làm. Ví dụ này chuyển một số, chuỗi này sau đó sẽ in ra

import threading

def worker[num]:
    """thread worker function"""
    print 'Worker: %s' % num
    return

threads = []
for i in range[5]:
    t = threading.Thread[target=worker, args=[i,]]
    threads.append[t]
    t.start[]

Đối số số nguyên hiện được bao gồm trong thông báo được in bởi mỗi luồng

    $ python -u threading_simpleargs.py

Worker: 0
Worker: 1
Worker: 2
Worker: 3
Worker: 4

Xác định chủ đề hiện tại

Sử dụng các đối số để xác định hoặc đặt tên cho luồng là rườm rà và không cần thiết. Mỗi phiên bản Chủ đề có một tên với giá trị mặc định có thể được thay đổi khi chủ đề được tạo. Các luồng đặt tên rất hữu ích trong các quy trình máy chủ với nhiều luồng dịch vụ xử lý các hoạt động khác nhau

import threading
import time

def worker[]:
    print threading.currentThread[].getName[], 'Starting'
    time.sleep[2]
    print threading.currentThread[].getName[], 'Exiting'

def my_service[]:
    print threading.currentThread[].getName[], 'Starting'
    time.sleep[3]
    print threading.currentThread[].getName[], 'Exiting'

t = threading.Thread[name='my_service', target=my_service]
w = threading.Thread[name='worker', target=worker]
w2 = threading.Thread[target=worker] # use default name

w.start[]
w2.start[]
t.start[]

Đầu ra gỡ lỗi bao gồm tên của chuỗi hiện tại trên mỗi dòng. Các dòng có "Chủ đề-1" trong cột tên chủ đề tương ứng với chủ đề chưa được đặt tên w2

________số 8

Hầu hết các chương trình không sử dụng in để gỡ lỗi. Mô-đun ghi nhật ký hỗ trợ nhúng tên luồng vào mọi thông báo tường trình bằng cách sử dụng mã định dạng %[threadName]s. Bao gồm tên chuỗi trong thông báo tường trình giúp dễ dàng theo dõi các thông báo đó trở lại nguồn của chúng

import logging
import threading
import time

logging.basicConfig[level=logging.DEBUG,
                    format='[%[levelname]s] [%[threadName]-10s] %[message]s',
                    ]

def worker[]:
    logging.debug['Starting']
    time.sleep[2]
    logging.debug['Exiting']

def my_service[]:
    logging.debug['Starting']
    time.sleep[3]
    logging.debug['Exiting']

t = threading.Thread[name='my_service', target=my_service]
w = threading.Thread[name='worker', target=worker]
w2 = threading.Thread[target=worker] # use default name

w.start[]
w2.start[]
t.start[]

ghi nhật ký cũng an toàn cho luồng, vì vậy các thông báo từ các luồng khác nhau được giữ riêng biệt ở đầu ra

#!/usr/bin/env python

import threading
import time


class MyThread[threading.Thread]:
    def run[self]:                                         # Default called function with mythread.start[]
        print["{} started!".format[self.getName[]]]        # "Thread-x started!"
        time.sleep[1]                                      # Pretend to work for a second
        print["{} finished!".format[self.getName[]]]       # "Thread-x finished!"

def main[]:
    for x in range[4]:                                     # Four times...
        mythread = MyThread[name = "Thread-{}".format[x]]  # ...Instantiate a thread and pass a unique ID to it
        mythread.start[]                                   # ...Start the thread, run method will be invoked
        time.sleep[.9]                                     # ...Wait 0.9 seconds before starting another

if __name__ == '__main__':
    main[]
0

daemon vs. Chủ đề không phải Daemon

Cho đến thời điểm này, các chương trình ví dụ đã ngầm chờ thoát cho đến khi tất cả các luồng hoàn thành công việc của chúng. Đôi khi các chương trình sinh ra một luồng dưới dạng daemon chạy mà không chặn chương trình chính thoát ra. Sử dụng luồng daemon rất hữu ích cho các dịch vụ khi không có cách nào dễ dàng để ngắt luồng hoặc khi để luồng chết giữa chừng thì dữ liệu sẽ không bị mất hoặc hỏng [ví dụ: luồng tạo ra "nhịp tim" cho . Để đánh dấu một luồng là daemon, hãy gọi phương thức setDaemon[] của nó với đối số boolean. Mặc định là các luồng không phải là daemon, do đó, việc chuyển True sẽ bật chế độ daemon

#!/usr/bin/env python

import threading
import time


class MyThread[threading.Thread]:
    def run[self]:                                         # Default called function with mythread.start[]
        print["{} started!".format[self.getName[]]]        # "Thread-x started!"
        time.sleep[1]                                      # Pretend to work for a second
        print["{} finished!".format[self.getName[]]]       # "Thread-x finished!"

def main[]:
    for x in range[4]:                                     # Four times...
        mythread = MyThread[name = "Thread-{}".format[x]]  # ...Instantiate a thread and pass a unique ID to it
        mythread.start[]                                   # ...Start the thread, run method will be invoked
        time.sleep[.9]                                     # ...Wait 0.9 seconds before starting another

if __name__ == '__main__':
    main[]
1

Lưu ý rằng đầu ra không bao gồm thông báo "Thoát" từ luồng daemon, vì tất cả các luồng không phải daemon [bao gồm cả luồng chính] đều thoát trước khi luồng daemon thức dậy sau giấc ngủ thứ hai của nó

#!/usr/bin/env python

import threading
import time


class MyThread[threading.Thread]:
    def run[self]:                                         # Default called function with mythread.start[]
        print["{} started!".format[self.getName[]]]        # "Thread-x started!"
        time.sleep[1]                                      # Pretend to work for a second
        print["{} finished!".format[self.getName[]]]       # "Thread-x finished!"

def main[]:
    for x in range[4]:                                     # Four times...
        mythread = MyThread[name = "Thread-{}".format[x]]  # ...Instantiate a thread and pass a unique ID to it
        mythread.start[]                                   # ...Start the thread, run method will be invoked
        time.sleep[.9]                                     # ...Wait 0.9 seconds before starting another

if __name__ == '__main__':
    main[]
2

Để đợi cho đến khi một luồng daemon hoàn thành công việc của nó, hãy sử dụng phương thức join[]

$ python threading_simple.py

Worker
Worker
Worker
Worker
Worker
0

Đợi chuỗi daemon thoát bằng cách sử dụng tham gia [] có nghĩa là nó có cơ hội tạo thông báo "Đang thoát"

$ python threading_simple.py

Worker
Worker
Worker
Worker
Worker
1

Theo mặc định, khối join[] vô thời hạn. Cũng có thể chuyển một đối số hết thời gian chờ [một số float biểu thị số giây chờ chuỗi không hoạt động]. Nếu chuỗi không hoàn thành trong khoảng thời gian chờ, tham gia [] vẫn trả về

$ python threading_simple.py

Worker
Worker
Worker
Worker
Worker
2

Vì thời gian chờ đã qua ít hơn thời gian mà luồng daemon ngủ, nên luồng vẫn "sống" sau khi hàm join[] trả về

$ python threading_simple.py

Worker
Worker
Worker
Worker
Worker
3

Liệt kê tất cả các chủ đề

Không cần thiết phải giữ lại một xử lý rõ ràng cho tất cả các luồng daemon để đảm bảo chúng đã hoàn thành trước khi thoát khỏi quy trình chính. enumerate[] trả về danh sách các phiên bản Chủ đề đang hoạt động. Danh sách này bao gồm luồng hiện tại và vì không được phép tham gia luồng hiện tại [nó dẫn đến tình huống bế tắc], nên phải bỏ qua

$ python threading_simple.py

Worker
Worker
Worker
Worker
Worker
4

Vì nhân viên đang ngủ trong một khoảng thời gian ngẫu nhiên, đầu ra từ chương trình này có thể thay đổi. Nó sẽ trông giống như thế này

$ python threading_simple.py

Worker
Worker
Worker
Worker
Worker
5

Chủ đề phân lớp

Khi khởi động, một Chủ đề thực hiện một số khởi tạo cơ bản và sau đó gọi phương thức run[] của nó, phương thức này gọi hàm mục tiêu được truyền cho hàm tạo. Để tạo một lớp con của Chủ đề, hãy ghi đè run[] để làm bất cứ điều gì cần thiết

$ python threading_simple.py

Worker
Worker
Worker
Worker
Worker
6

Giá trị trả về của run[] bị bỏ qua

$ python threading_simple.py

Worker
Worker
Worker
Worker
Worker
7

Bởi vì các giá trị args và kwargs được truyền cho hàm tạo Thread được lưu trong các biến riêng tư, nên chúng không dễ dàng truy cập được từ một lớp con. Để chuyển các đối số cho một loại chuỗi tùy chỉnh, hãy xác định lại hàm tạo để lưu các giá trị trong một thuộc tính thể hiện có thể nhìn thấy trong lớp con

$ python threading_simple.py

Worker
Worker
Worker
Worker
Worker
8

MyThreadWithArgs sử dụng API giống như Chủ đề, nhưng một lớp khác có thể dễ dàng thay đổi phương thức hàm tạo để nhận nhiều đối số hơn hoặc khác nhau liên quan trực tiếp hơn đến mục đích của luồng, như với bất kỳ lớp nào khác

$ python threading_simple.py

Worker
Worker
Worker
Worker
Worker
9

Báo hiệu giữa các chủ đề

Mặc dù mục đích của việc sử dụng nhiều luồng là loại bỏ các hoạt động riêng biệt để chạy đồng thời, nhưng đôi khi điều quan trọng là có thể đồng bộ hóa các hoạt động trong hai hoặc nhiều luồng. Một cách đơn giản để giao tiếp giữa các luồng là sử dụng các đối tượng Sự kiện. Sự kiện quản lý cờ nội bộ mà người gọi có thể đặt [] hoặc xóa []. Các chủ đề khác có thể đợi [] để cờ được đặt [], chặn tiến trình một cách hiệu quả cho đến khi được phép tiếp tục

import threading

def worker[num]:
    """thread worker function"""
    print 'Worker: %s' % num
    return

threads = []
for i in range[5]:
    t = threading.Thread[target=worker, args=[i,]]
    threads.append[t]
    t.start[]
0

Phương thức wait[] nhận một đối số biểu thị số giây chờ sự kiện trước khi hết thời gian. Nó trả về một giá trị boolean cho biết sự kiện có được thiết lập hay không, để người gọi biết tại sao hàm wait[] được trả về. Phương thức isSet[] có thể được sử dụng riêng trong sự kiện mà không sợ bị chặn

Trong ví dụ này, wait_for_event_timeout[] kiểm tra trạng thái sự kiện mà không chặn vô thời hạn. Hàm wait_for_event[] chặn lệnh gọi hàm wait[], hàm này không trả về cho đến khi trạng thái sự kiện thay đổi

import threading

def worker[num]:
    """thread worker function"""
    print 'Worker: %s' % num
    return

threads = []
for i in range[5]:
    t = threading.Thread[target=worker, args=[i,]]
    threads.append[t]
    t.start[]
1

Kiểm soát quyền truy cập vào tài nguyên

Ngoài việc đồng bộ hóa hoạt động của các luồng, điều quan trọng là có thể kiểm soát quyền truy cập vào các tài nguyên được chia sẻ để ngăn ngừa hỏng hoặc bỏ sót dữ liệu. Các cấu trúc dữ liệu có sẵn của Python [danh sách, từ điển, v.v. ] an toàn cho luồng do tác dụng phụ của việc có mã byte nguyên tử để thao tác với chúng [GIL không được phát hành ở giữa bản cập nhật]. Các cấu trúc dữ liệu khác được triển khai trong Python hoặc các loại đơn giản hơn như số nguyên và số float, không có sự bảo vệ đó. Để bảo vệ chống truy cập đồng thời vào một đối tượng, hãy sử dụng đối tượng Khóa

import threading

def worker[num]:
    """thread worker function"""
    print 'Worker: %s' % num
    return

threads = []
for i in range[5]:
    t = threading.Thread[target=worker, args=[i,]]
    threads.append[t]
    t.start[]
2

Trong ví dụ này, hàm worker[] tăng phiên bản Counter, quản lý Khóa để ngăn hai luồng thay đổi trạng thái bên trong cùng một lúc. Nếu Khóa không được sử dụng, có khả năng thiếu thay đổi đối với thuộc tính giá trị

import threading

def worker[num]:
    """thread worker function"""
    print 'Worker: %s' % num
    return

threads = []
for i in range[5]:
    t = threading.Thread[target=worker, args=[i,]]
    threads.append[t]
    t.start[]
3

Để tìm hiểu xem một luồng khác có nhận được khóa mà không giữ luồng hiện tại hay không, hãy chuyển Sai cho đối số chặn để thu được[]. Trong ví dụ tiếp theo, worker[] cố gắng lấy khóa ba lần riêng biệt và đếm xem nó phải thực hiện bao nhiêu lần để làm như vậy. Đồng thời, lock_holder[] xoay vòng giữa giữ và nhả khóa, với các khoảng dừng ngắn ở mỗi trạng thái được sử dụng để mô phỏng tải

import threading

def worker[num]:
    """thread worker function"""
    print 'Worker: %s' % num
    return

threads = []
for i in range[5]:
    t = threading.Thread[target=worker, args=[i,]]
    threads.append[t]
    t.start[]
4

Phải mất worker[] hơn ba lần lặp để có được khóa ba lần riêng biệt

import threading

def worker[num]:
    """thread worker function"""
    print 'Worker: %s' % num
    return

threads = []
for i in range[5]:
    t = threading.Thread[target=worker, args=[i,]]
    threads.append[t]
    t.start[]
5

Khóa đăng nhập lại

Các đối tượng Khóa thông thường không thể được lấy nhiều lần, thậm chí bởi cùng một chuỗi. Điều này có thể gây ra các tác dụng phụ không mong muốn nếu khóa được truy cập bởi nhiều chức năng trong cùng một chuỗi cuộc gọi

import threading

def worker[num]:
    """thread worker function"""
    print 'Worker: %s' % num
    return

threads = []
for i in range[5]:
    t = threading.Thread[target=worker, args=[i,]]
    threads.append[t]
    t.start[]
6

Trong trường hợp này, vì cả hai chức năng đang sử dụng cùng một khóa toàn cầu và một hàm gọi hàm kia, nên lần mua thứ hai không thành công và sẽ bị chặn bằng cách sử dụng các đối số mặc định để thu được[]

import threading

def worker[num]:
    """thread worker function"""
    print 'Worker: %s' % num
    return

threads = []
for i in range[5]:
    t = threading.Thread[target=worker, args=[i,]]
    threads.append[t]
    t.start[]
7

Trong trường hợp mã riêng biệt từ cùng một chuỗi cần "lấy lại" khóa, hãy sử dụng RLock để thay thế

import threading

def worker[num]:
    """thread worker function"""
    print 'Worker: %s' % num
    return

threads = []
for i in range[5]:
    t = threading.Thread[target=worker, args=[i,]]
    threads.append[t]
    t.start[]
8

Thay đổi duy nhất đối với mã từ ví dụ trước là thay thế RLock cho Lock

import threading

def worker[num]:
    """thread worker function"""
    print 'Worker: %s' % num
    return

threads = []
for i in range[5]:
    t = threading.Thread[target=worker, args=[i,]]
    threads.append[t]
    t.start[]
9

Khóa dưới dạng Trình quản lý ngữ cảnh

Các khóa triển khai API trình quản lý ngữ cảnh và tương thích với câu lệnh with. Sử dụng with loại bỏ nhu cầu lấy và giải phóng khóa một cách rõ ràng

    $ python -u threading_simpleargs.py

Worker: 0
Worker: 1
Worker: 2
Worker: 3
Worker: 4
0

Hai chức năng worker_with[] và worker_no_with[] quản lý khóa theo cách tương đương

    $ python -u threading_simpleargs.py

Worker: 0
Worker: 1
Worker: 2
Worker: 3
Worker: 4
1

Đồng bộ chủ đề

Ngoài việc sử dụng Sự kiện, một cách khác để đồng bộ hóa chuỗi là sử dụng đối tượng Điều kiện. Vì Điều kiện sử dụng Khóa nên nó có thể được liên kết với tài nguyên được chia sẻ. Điều này cho phép các chủ đề chờ tài nguyên được cập nhật. Trong ví dụ này, các chủ đề của người tiêu dùng [] đợi [] để Điều kiện được đặt trước khi tiếp tục. Chủ đề của nhà sản xuất [] chịu trách nhiệm đặt điều kiện và thông báo cho các chủ đề khác rằng họ có thể tiếp tục

    $ python -u threading_simpleargs.py

Worker: 0
Worker: 1
Worker: 2
Worker: 3
Worker: 4
2

Các luồng sử dụng with để lấy khóa được liên kết với Điều kiện. Sử dụng các phương thức thu được [] và phát hành [] một cách rõ ràng cũng hoạt động

    $ python -u threading_simpleargs.py

Worker: 0
Worker: 1
Worker: 2
Worker: 3
Worker: 4
3

Hạn chế quyền truy cập đồng thời vào tài nguyên

Đôi khi sẽ hữu ích khi cho phép nhiều nhân viên truy cập vào một tài nguyên cùng một lúc, trong khi vẫn giới hạn tổng số lượng. Ví dụ: nhóm kết nối có thể hỗ trợ số lượng kết nối đồng thời cố định hoặc ứng dụng mạng có thể hỗ trợ số lượng tải xuống đồng thời cố định. Semaphore là một cách để quản lý các kết nối đó

    $ python -u threading_simpleargs.py

Worker: 0
Worker: 1
Worker: 2
Worker: 3
Worker: 4
4

Trong ví dụ này, lớp ActivePool chỉ phục vụ như một cách thuận tiện để theo dõi luồng nào có thể chạy tại một thời điểm nhất định. Nhóm tài nguyên thực sẽ phân bổ kết nối hoặc một số giá trị khác cho luồng mới hoạt động và lấy lại giá trị khi luồng hoàn thành. Ở đây, nó chỉ được sử dụng để giữ tên của các luồng đang hoạt động để chỉ ra rằng chỉ có năm luồng đang chạy đồng thời

    $ python -u threading_simpleargs.py

Worker: 0
Worker: 1
Worker: 2
Worker: 3
Worker: 4
5

Dữ liệu dành riêng cho chủ đề

Mặc dù một số tài nguyên cần được khóa để nhiều luồng có thể sử dụng chúng, nhưng những tài nguyên khác cần được bảo vệ để chúng bị ẩn khỏi chế độ xem trong các luồng không "sở hữu" chúng. Hàm local[] tạo một đối tượng có khả năng ẩn các giá trị khỏi chế độ xem trong các luồng riêng biệt

    $ python -u threading_simpleargs.py

Worker: 0
Worker: 1
Worker: 2
Worker: 3
Worker: 4
6

Lưu ý rằng local_data. giá trị không xuất hiện cho bất kỳ luồng nào cho đến khi nó được đặt trong luồng đó

    $ python -u threading_simpleargs.py

Worker: 0
Worker: 1
Worker: 2
Worker: 3
Worker: 4
7

Để khởi tạo cài đặt sao cho tất cả các chuỗi bắt đầu với cùng một giá trị, hãy sử dụng một lớp con và đặt các thuộc tính trong __init__[]

Nhập luồng trong Python là gì?

Phân luồng trong python được dùng để chạy nhiều luồng [tác vụ, lệnh gọi hàm] cùng một lúc . Lưu ý rằng điều này không có nghĩa là chúng được thực thi trên các CPU khác nhau. Các luồng Python sẽ KHÔNG làm cho chương trình của bạn nhanh hơn nếu nó đã sử dụng 100% thời gian CPU.

Có thể phân luồng trong Python không?

Python không hỗ trợ đa luồng vì Python trên trình thông dịch Cpython không hỗ trợ thực thi đa lõi thực sự thông qua đa luồng. Tuy nhiên, Python có thư viện luồng . GIL không ngăn luồng.

Làm thế nào là luồng được thực hiện trong Python?

Trong mô-đun luồng, để chạy hoặc thực thi luồng, bạn sử dụng phương thức start[], phương thức này chỉ chịu trách nhiệm chạy luồng. You will also use the join method, which means that wait until all the thread execution is complete.

Chủ đề Nhập Python có an toàn không?

Quá trình nhập thông thường an toàn theo luồng vì chúng có khóa nhập trước khi thực thi và giải phóng sau khi quá trình nhập hoàn tất. Nếu bạn thêm các mục nhập tùy chỉnh của riêng mình bằng cách sử dụng các móc có sẵn, hãy đảm bảo thêm sơ đồ khóa này vào đó.

Chủ Đề