Tương lai python

Với tôi thì có. Cái gì mà khá vui và cũng hơi khó hiểu ở cái tên tương lai trong tên của mô-đun này. And here as as module duy nhất trong package Concurrent. Trong một số khung nổi bật về học máy hầu hết đều có sử dụng đến mô-đun này

Ok, không dài thêm dòng nữa, chúng ta sẽ đi thẳng vào cách mà mô-đun này được sử dụng và tại sao nó lại có liên quan đến Lập trình đồng thời

Nhiệm vụ I/O-Bound

Đây là những công việc thông thường liên quan đến Mạng, ví dụ như

  • Thực hiện một lời gọi API bên ngoài
  • Tải tập tin trên Internet

Ngoài ra, các tác vụ I/O bị ràng buộc cũng có thể là các tác vụ thường xuất hiện hàng ngày trên một chiếc máy tính bình thường như đọc, ghi, lưu tệp vào đĩa cứng của bạn hoặc vài thứ về thiết bị xuất chuẩn, thiết bị xuất chuẩn. Một ví dụ đó là câu lệnh

from time import sleep


def just_sleep[n: int = 3] -> None:
    """ Do nothing on n seconds [I/O bound]
    """
    print[f"Be waiting {n} seconds ..."]

    # I/O bound task
    sleep[n]

    print[f"Done.\n"]


if __name__ == '__main__':
    for _ in range[5]:
        # "pause" the program 3s by default
        just_sleep[]
1 trong Python

Ví dụ nhỏ

Cùng xem qua đoạn mã dưới đây nào

def just_sleep[n: int = 3] -> None:
    """ Do nothing on n seconds [I/O bound]
    """
    print[f"Be waiting {n} seconds ..."]

    # I/O bound task
    sleep[n]

    print[f"Done.\n"]

Ở dây, tôi định nghĩa một chức năng có nhiệm vụ chính là cho phép kéo dài chương trình một khoảng thời gian n giây trước khi ra thông báo có nội dung là “Hoàn tất. ” và xuống dòng

Please run this function in a script python menu as below here

from time import sleep


def just_sleep[n: int = 3] -> None:
    """ Do nothing on n seconds [I/O bound]
    """
    print[f"Be waiting {n} seconds ..."]

    # I/O bound task
    sleep[n]

    print[f"Done.\n"]


if __name__ == '__main__':
    for _ in range[5]:
        # "pause" the program 3s by default
        just_sleep[]

Về cơ bản, chúng ta đang yêu cầu chương trình tạm dừng chương trình 5 lần và mỗi lần dừng khoảng 3 giây. Một cách tính tuyến tính thì chúng ta sẽ phải mất khoảng đâu đó tầm 5 x 3 = 15 [giây] để đoạn script này chạy xong. Để chắc chắn rằng tôi không thắc mắc logic lỗi gì ở đây thì chúng ta nên thực hiện một công đoạn nhỏ nữa là thêm

from time import sleep


def just_sleep[n: int = 3] -> None:
    """ Do nothing on n seconds [I/O bound]
    """
    print[f"Be waiting {n} seconds ..."]

    # I/O bound task
    sleep[n]

    print[f"Done.\n"]


if __name__ == '__main__':
    for _ in range[5]:
        # "pause" the program 3s by default
        just_sleep[]
2 vào chương trình này

# thread_pool1.py

import time
from time import sleep


def just_sleep[n: int = 3] -> None:
    """ Do nothing on n seconds [I/O bound]
    """
    print[f"Be waiting {n} seconds ..."]

    # I/O bound task
    sleep[n]

    print[f"Done.\n"]


if __name__ == '__main__':
    start_time = time.time[]

    for _ in range[5]:
        # "pause" the program 3s by default
        just_sleep[]

    print["Elapsed time: {:.2f} seconds".format[time.time[] - start_time]]

Cùng chạy thử coi nào

đồng thời. tương lai đến để chơi

Bạn có thử đặt câu hỏi rằng làm sao chúng ta có thể làm được gì trong quá trình chờ đợi 3 giây kia được không?

Hãy lấy từ cuộc sống thực nhé, bạn sẽ làm gì khi đang đứng chờ xe buýt, hay chờ đến lượt mình tính tiền trong siêu thị. Có thể là bất cứ điều gì đúng chứ nhỉ?, lướt fb chẳng hạn, nhưng tôi tin là nó sẽ khiến bạn đỡ thấy chán hơn trong lúc đó. Ở đây cũng vậy, trong lúc

from time import sleep


def just_sleep[n: int = 3] -> None:
    """ Do nothing on n seconds [I/O bound]
    """
    print[f"Be waiting {n} seconds ..."]

    # I/O bound task
    sleep[n]

    print[f"Done.\n"]


if __name__ == '__main__':
    for _ in range[5]:
        # "pause" the program 3s by default
        just_sleep[]
3 thì chúng ta hoàn toàn có thể thực hiện một
from time import sleep


def just_sleep[n: int = 3] -> None:
    """ Do nothing on n seconds [I/O bound]
    """
    print[f"Be waiting {n} seconds ..."]

    # I/O bound task
    sleep[n]

    print[f"Done.\n"]


if __name__ == '__main__':
    for _ in range[5]:
        # "pause" the program 3s by default
        just_sleep[]
3 khác. Tại sao không khi Python hoàn toàn cho phép bạn thực hiện điều đó. 🔥🔥🔥

Ngoại lệ

Thực ra, chúng ta không thể thực hiện một lúc nhiều hơn một nhiệm vụ tại một thời điểm trong Python khi mà cái được gọi là GLOBAL INTERPRETER LOCK [GIL] sẽ ngăn chương trình thực thi “bài hát” nhiều nhiệm vụ một lúc

Về mặt lịch sử, thì Python được thiết kế sao cho nó chắc chắn được rằng thông dịch viên chỉ thực hiện một nhiệm vụ tại một thời điểm trên cùng một chủ đề. Tuy nhiên cũng đã có những nỗ lực để loại bỏ GIL ở Python 1. 5. Tuy nhiên, theo Guido van van Rossum [người tạo ngôn ngữ lập trình Python] đã có những cảnh báo định hướng tốt nhất về việc loại bỏ hoàn toàn GIL, khi nó có những tác động về mặt hiệu năng nhất định

Hãy sử dụng Class để thực hiện điều mà ta mong muốn, đó là chạy nhiều chức năng

from time import sleep


def just_sleep[n: int = 3] -> None:
    """ Do nothing on n seconds [I/O bound]
    """
    print[f"Be waiting {n} seconds ..."]

    # I/O bound task
    sleep[n]

    print[f"Done.\n"]


if __name__ == '__main__':
    for _ in range[5]:
        # "pause" the program 3s by default
        just_sleep[]
0 một cách không đồng bộ

def future_fn[fn]:
    """ Using ThreadPoolExecutor class to
    create a convention thread manager
    """
    with ThreadPoolExecutor[max_workers=5] as executor:
        """
        .map[] function in concurrent futures is just the same
        as .map built-in function of Python language

        arg1: fn [callable] to execute
        arg2: an iterable to pass in as argument for the above fn

        """
        executor.map[fn, [3]*5]

Ở đây, chúng ta sử dụng hàm của lớp ThreadPoolExecutor, là một lớp con của lớp trừu tượng

# thread_pool2.py

import time
from time import sleep
from concurrent.futures import ThreadPoolExecutor


def just_sleep[n: int = 3] -> None:
    """ Do nothing on n seconds [I/O bound]
    """
    print[f"Be waiting {n} seconds ..."]

    # I/O bound task
    sleep[n]

    print[f"Done.\n"]


def future_fn[fn]:
    """ Using ThreadPoolExecutor class to
    create a convention thread manager
    """
    with ThreadPoolExecutor[max_workers=5] as executor:
        """
        .map[] function in concurrent futures is just the same
        as .map built-in function of Python language

        arg1: fn [callable] to execute
        arg2: an iterable to pass in as argument for the above fn

        """
        executor.map[fn, [3]*5]


if __name__ == '__main__':
    start_time = time.time[]

    # current_fn[just_sleep]
    future_fn[just_sleep]

    print[f"Elapsed time: {time.time[] - start_time} seconds"]

Please run back and view any results

3 giây. That is running time when using ThreadPoolExecutor

Một số giải thích

Chúng ta sẽ thảo luận một chút về công việc tại sao, khi sử dụng module futures lại cho kết quả như vậy

Như tôi đã đề cập ở trên về vấn đề GIL. Bạn sẽ không thể thực hiện nhiều hơn là một nhiệm vụ tại một thời điểm và cách để vượt qua vấn đề này là sử dụng đến cách tiếp cận khác và vượt ra khỏi phạm vi của bài viết này. [bật mí. sử dụng đa xử lý mô-đun]

Đúng vậy, với nhiệm vụ ràng buộc I/O thì chúng ta hoàn toàn có một cách để vượt qua giới hạn chế độ này. Hãy cùng nhau xem qua slide bên dưới, được lấy từ bài nói chuyện của David Beazley [slide]

Như bạn có thể nhìn thấy, khi một luồng thực hiện công việc của nó và nếu tồn tại các hoạt động I/O [không giới hạn như tệp đọc-ghi], thì tại thời điểm đó GIL đã được giải nén và nếu chương trình của chúng tôi

Luồng, đa xử lý và đồng thời. tương lai

Thật ra, module concurrent. futures chỉ là một mô-đun cấp cao. Nó hoàn toàn dựa trên hai mô-đun khác của Python, đó là phân luồng và đa xử lý. Nó cho phép những người mới bắt đầu làm quen với lập trình đồng thời có một công cụ để có thể chơi với công việc xử lý tác vụ đa luồng hoặc tận dụng tính toán đa lõi của những CPU đời mới một cách dễ dàng và nhanh chóng. Tuy nhiên, đổi lại thì chúng tôi sẽ không thể tác động sâu vào bên trong lõi của hai mô-đun kia trong những vấn đề phức tạp hơn

Một số ví dụ khác

import concurrent.futures
import urllib.request


URLS = ['//dantri.com.vn/',
        '//vnexpress.net/',
        '//kenh14.vn/',
        '//zingnews.vn/',
        '//genk.vn/']


# Retrieve a single page and report the URL and contents
def load_url[url, timeout]:
    with urllib.request.urlopen[url, timeout=timeout] as conn:
        return conn.read[]


# We can use a with statement to ensure threads are cleaned up promptly
with concurrent.futures.ThreadPoolExecutor[max_workers=5] as executor:
    # Start the load operations and mark each future with its URL
    future_to_url = dict[]

    for url in URLS:
        future = executor.submit[load_url, url, 60]
        future_to_url.update[{future: url}] # [*]

    for future in concurrent.futures.as_completed[future_to_url]:
        url = future_to_url[future]
        try:
            data = future.result[]
        except Exception as exc:
            print['%r generated an exception: %s' % [url, exc]]
        else:
            print['%r page is %d bytes' % [url, len[data]]]

Ví dụ trên được lấy từ , được tôi chỉnh sửa lại một chút để làm rõ ràng hơn cho người đọc. Nhiệm vụ chính của chức năng

from time import sleep


def just_sleep[n: int = 3] -> None:
    """ Do nothing on n seconds [I/O bound]
    """
    print[f"Be waiting {n} seconds ..."]

    # I/O bound task
    sleep[n]

    print[f"Done.\n"]


if __name__ == '__main__':
    for _ in range[5]:
        # "pause" the program 3s by default
        just_sleep[]
1 là thực hiện các yêu cầu GET đến một số trang báo mạng nổi bật ở Việt Nam và sau khi nhận được phản hồi thì tốc độ phản hồi của byte theo từng trang. Đương nhiên là ví dụ này được chạy hoàn toàn bất đồng bộ

Ở ví dụ này, chúng ta sử dụng hai chức năng khác đó là và. Hàm đầu tiên thuộc về phân lớp ThreadPoolExecutor, còn cái sau là hàm cấp mô-đun. Như ví dụ trước đó của chúng ta, chức năng. map[] used to run concurrent. Có một sự khác biệt ở chức năng này đó là nó chỉ cho phép chúng ta sử dụng duy nhất một chức năng kiểm soát các dữ liệu khác nhau, hoàn toàn giống như cách mà. map[] phương thức tích hợp sẵn của Python được sử dụng. Trong khi đó, ở ví dụ hiện tại, với việc sử dụng. submit[], chúng ta hoàn toàn có thể chạy thông qua một vòng lặp cho và thực hiện chuyển vào các chức năng khác nhau mà chúng ta muốn thực hiện. Như vậy nó sẽ linh hoạt hơn. Như vậy thì ở ví dụ này, chúng ta chỉ sử dụng duy nhất một chức năng cho việc chạy bất đồng bộ đó là

from time import sleep


def just_sleep[n: int = 3] -> None:
    """ Do nothing on n seconds [I/O bound]
    """
    print[f"Be waiting {n} seconds ..."]

    # I/O bound task
    sleep[n]

    print[f"Done.\n"]


if __name__ == '__main__':
    for _ in range[5]:
        # "pause" the program 3s by default
        just_sleep[]
1. Ngoài ra, giá trị trả về của chức năng. submit[] sẽ là một đối tượng, về cơ bản là một đối tượng mà cho phép chúng ta giữ nguyên trạng thái cũng như kết quả sau khi nhiệm vụ được thực hiện hoàn thành trong cơ chế bất đồng bộ của mô-đun tương lai

Ở dòng [*] trong đoạn mã trên, ta tạo ra một từ điển để ánh xạ từng URL đến các đối tượng tương lai tương ứng của chúng để thuận tiện cho việc kiểm tra được kết quả trả về cũng như quá trình gỡ lỗi

from time import sleep


def just_sleep[n: int = 3] -> None:
    """ Do nothing on n seconds [I/O bound]
    """
    print[f"Be waiting {n} seconds ..."]

    # I/O bound task
    sleep[n]

    print[f"Done.\n"]


if __name__ == '__main__':
    for _ in range[5]:
        # "pause" the program 3s by default
        just_sleep[]
3 may be function quan trọng nhất ở đoạn này. tại sao? . Và đó là lý do mà
from time import sleep


def just_sleep[n: int = 3] -> None:
    """ Do nothing on n seconds [I/O bound]
    """
    print[f"Be waiting {n} seconds ..."]

    # I/O bound task
    sleep[n]

    print[f"Done.\n"]


if __name__ == '__main__':
    for _ in range[5]:
        # "pause" the program 3s by default
        just_sleep[]
3 có mặt trong mô-đun này

Kết quả

Tôi sẽ tạm dừng bài blog ở đây. Chủ để về Lập trình đồng thời không phải sớm một chiều chúng ta có thể hiểu toàn diện được, tuy nhiên, bài viết này giống như một cái bản tóm tắt cơ bản về cách chúng ta có thể thực hiện CP mà không cần quá nhiều nỗ lực . tương lai Python và được đọc từ cuốn sách rất hay của Luciano Ramalho với tiêu đề là Fluent Python

Chủ Đề