Lộ trình học đồng thời Python
"Trong hơn một thập kỷ, các nhà tiên tri đã lên tiếng tranh luận rằng tổ chức của một máy tính duy nhất đã đạt đến giới hạn của nó và những tiến bộ thực sự quan trọng chỉ có thể được thực hiện bằng cách kết nối nhiều máy tính với nhau. "
Tận dụngthehầu hết phần mềm của bạn là điều mà tất cả các nhà phát triển đều phấn đấu và đồng thời, và . Thông qua việc áp dụng cẩn thận các khái niệm đồng thời vào các ứng dụng đơn luồng trước đây của chúng tôi, chúng tôi có thể bắt đầu nhận ra toàn bộ sức mạnh của phần cứng cơ bản và cố gắng giải quyết các vấn đề không thể giải quyết được trong những ngày qua. Với đồng thời, chúng tôi có thể cải thiện hiệu suất nhận thức của các ứng dụng của mình bằng cách xử lý đồng thời các yêu cầu và cập nhật giao diện người dùng thay vì chỉ bị treo cho đến khi hoàn tất tác vụ phụ trợ. Đã qua rồi cái thời của các chương trình không phản hồi không cho bạn biết liệu chúng đã bị lỗi hay vẫn đang hoạt động âm thầm Mặc dù vậy, sự cải thiện về hiệu suất của các ứng dụng của chúng tôi phải trả giá đắt. Bằng cách chọn triển khai các hệ thống theo kiểu đồng thời, chúng tôi thường thấy độ phức tạp tổng thể của mã tăng lên và nguy cơ lỗi xuất hiện trong mã mới này tăng cao. Để triển khai thành công các hệ thống đồng thời, trước tiên chúng ta phải hiểu một số nguyên tắc và khái niệm đồng thời chính ở cấp độ sâu hơn để đảm bảo rằng các ứng dụng của chúng ta an toàn trước các mối đe dọa cố hữu mới này Trong chương này, tôi sẽ đề cập đến một số chủ đề cơ bản mà mọi lập trình viên cần biết trước khi tiếp tục phát triển các hệ thống phần mềm đồng thời. Điều này bao gồm những điều sau đây
Lịch sử đồng thờiTính đồng thời thực sự bắt nguồn từ công việc ban đầu trên đường sắt và điện báo, đó là lý do tại sao những cái tên như semaphore hiện đang được sử dụng. Về cơ bản, cần phải xử lý nhiều đoàn tàu trên cùng một hệ thống đường sắt theo cách mà mọi đoàn tàu sẽ đến đích một cách an toàn mà không gây thương vong. Chỉ trong những năm 1960, giới học thuật mới bắt đầu up quan tâm đến tính toán đồng thời và đó là Edsger W. Dijkstra, người được cho là đã xuất bản bài báo đầu tiên trong lĩnh vực này, trong đó ông đã xác định và giải quyết vấn đề loại trừ lẫn nhau. Dijkstra sau đó tiếp tục xác định các khái niệm đồng thời cơ bản, chẳng hạn như semaphores, loại trừ lẫn nhau và bế tắc cũng như Thuật toán đường đi ngắn nhất nổi tiếng của Dijkstra. Đồng thời, giống như hầu hết các lĩnh vực trong khoa học máy tính, vẫn là một lĩnh vực cực kỳ non trẻ khi so sánh với các lĩnh vực nghiên cứu khác như toán học, và bạn nên ghi nhớ điều này. Vẫn còn tiềm năng to lớn cho sự thay đổi trong lĩnh vực này và nó vẫn là một lĩnh vực thú vị cho tất cả mọi người--các học giả, nhà thiết kế ngôn ngữ và nhà phát triển--như nhau Việc giới thiệu nguyên mẫu đồng thời cấp cao và hỗ trợ ngôn ngữ bản địa tốt hơn đã thực sự cải thiện cách thức mà chúng tôi, với tư cách là kiến trúc sư phần mềm, triển khai các giải pháp đồng thời. Trong nhiều năm, điều này cực kỳ khó thực hiện, nhưng với sự ra đời của các API đồng thời mới cũng như các khung và ngôn ngữ hoàn thiện, nó bắt đầu trở nên dễ dàng hơn rất nhiều đối với chúng tôi với tư cách là nhà phát triển Các nhà thiết kế ngôn ngữ phải đối mặt với một thách thức khá lớn khi cố gắng triển khai đồng thời không chỉ an toàn mà còn hiệu quả và dễ viết cho người dùng ngôn ngữ đó. Các ngôn ngữ lập trình như Golang của Google, Rust và thậm chí cả Python đã có những bước tiến lớn trong lĩnh vực này và điều này giúp việc khai thác toàn bộ tiềm năng từ các máy mà chương trình của bạn chạy trên đó trở nên dễ dàng hơn rất nhiều. Chủ đề và đa luồngTrong phần này của cuốn sách, chúng ta sẽ tìm hiểu sơ qua về chuỗi là gì, cũng như cách thức chúng ta có thể . một chủ đề là gì?Một luồng có thể được định nghĩa là một luồng các lệnh có thứ tự mà các hệ điều hành có thể lên lịch để chạy như vậy. Thông thường, các luồng này nằm trong các quy trình và bao gồm bộ đếm chương trình, ngăn xếp và tập hợp các thanh ghi cũng như mã định danh. Các luồng này là đơn vị thực thi nhỏ nhất mà bộ xử lý có thể phân bổ thời gian. Các luồng có thể tương tác với các tài nguyên được chia sẻ và có thể giao tiếp giữa nhiều luồng. Họ cũng có thể chia sẻ bộ nhớ, đọc và ghi các địa chỉ bộ nhớ khác nhau, nhưng trong đó có một vấn đề. Khi hai luồng bắt đầu chia sẻ bộ nhớ và bạn không có cách nào để đảm bảo thứ tự thực thi của luồng, bạn có thể bắt đầu thấy các sự cố hoặc lỗi nhỏ cung cấp cho bạn các giá trị sai hoặc làm hệ thống của bạn bị sập hoàn toàn. Những vấn đề này chủ yếu là do điều kiện chủng tộc gây ra mà chúng ta sẽ đi sâu hơn trong Chương 4, Đồng bộ hóa giữa các luồng. Hình dưới đây cho thấy cách nhiều luồng có thể tồn tại trên nhiều CPU khác nhau Các loại chủ đềTrong một hệ điều hành điển hình, chúng ta thường có hai loại luồng riêng biệt
Python hoạt động ở cấp độ người dùng và do đó, mọi thứ chúng tôi trình bày trong cuốn sách này sẽ chủ yếu tập trung vào các chuỗi cấp độ người dùng này . Đa luồng là gì?Khi mọi người nói về bộ xử lý đa luồng, họ thường đề cập đến đến một bộ xử lý có thể chạy nhiều luồng đồng thời mà họ có thể thực hiện . Bối cảnh chuyển đổi này diễn ra trong một khoảng thời gian ngắn đến mức chúng ta có thể cho rằng có nhiều luồng đang chạy song song trong khi thực tế thì không phải như vậy . Khi cố gắng hiểu về đa luồng, tốt nhất bạn nên nghĩ về một chương trình đa luồng như một văn phòng. Trong một chương trình đơn luồng, sẽ chỉ có một người làm việc trong văn phòng này mọi lúc, xử lý tất cả các công việc một cách tuần tự. Điều này sẽ trở thành một vấn đề nếu chúng ta xem xét điều gì sẽ xảy ra khi người lao động đơn độc này sa lầy vào công việc giấy tờ hành chính và không thể chuyển sang công việc khác. Họ sẽ không thể đối phó và sẽ không thể đối phó với doanh số bán hàng mới sắp tới, do đó làm tiêu tốn tiền kinh doanh ẩn dụ của chúng tôi Với đa luồng, nhân viên đơn độc của chúng tôi trở thành một người đa nhiệm xuất sắc và có thể làm việc trên nhiều thứ vào những thời điểm khác nhau. Họ có thể đạt được tiến bộ trong một số thủ tục giấy tờ và sau đó chuyển ngữ cảnh sang một nhiệm vụ mới khi có điều gì đó bắt đầu ngăn cản họ thực hiện thêm công việc trên các thủ tục giấy tờ nói trên. Bằng cách có thể chuyển ngữ cảnh khi có thứ gì đó chặn họ, họ có thể làm được nhiều việc hơn trong một khoảng thời gian ngắn hơn và do đó giúp doanh nghiệp của chúng tôi kiếm được nhiều tiền hơn Trong ví dụ này, điều quan trọng cần lưu ý là chúng tôi vẫn chỉ giới hạn ở một công nhân hoặc lõi xử lý. Nếu chúng tôi muốn thử và cải thiện khối lượng công việc mà doanh nghiệp có thể thực hiện và hoàn thành công việc song song, thì chúng tôi sẽ phải thuê những công nhân hoặc quy trình khác như chúng tôi gọi họ trong Python Hãy xem một vài ưu điểm của luồng
Có một số nhược điểm quá, đó là như sau
quy trìnhCác quy trình về bản chất rất giống nhau với các luồng--chúng cho phép chúng tôi thực hiện hầu hết mọi thứ mà một luồng có thể làm--nhưng quy trình . Nếu chúng ta mở rộng phép loại suy văn phòng của mình hơn nữa, về cơ bản, điều này có nghĩa là nếu chúng ta có CPU bốn lõi, thì chúng ta có thể thuê hai thành viên nhóm bán hàng chuyên dụng và hai công nhân, và cả bốn người trong số họ sẽ có thể thực hiện công việc song song. Các quy trình cũng có khả năng xử lý nhiều việc cùng một lúc giống như nhân viên văn phòng đa luồng của chúng tôi. Các quy trình này chứa một luồng chính chính, nhưng có thể sinh ra nhiều luồng phụ, mỗi luồng chứa một bộ thanh ghi và ngăn xếp riêng. Chúng có thể trở thành đa luồng nếu bạn muốn. Tất cả các quy trình cung cấp mọi tài nguyên mà máy tính cần để thực thi chương trình Trong hình ảnh sau đây, bạn sẽ thấy hai sơ đồ cạnh nhau; . Bạn sẽ nhận thấy rằng quy trình bên trái chỉ chứa một luồng, còn được gọi là luồng chính. Quá trình bên phải chứa nhiều luồng, mỗi luồng có bộ thanh ghi và ngăn xếp riêng Với các quy trình, chúng tôi có thể cải thiện tốc độ của các chương trình trong các tình huống cụ thể trong đó các chương trình của chúng tôi bị ràng buộc bởi CPU và yêu cầu nhiều mã lực CPU hơn. Tuy nhiên, bằng cách sinh ra nhiều quy trình, chúng tôi phải đối mặt với những thách thức mới liên quan đến giao tiếp giữa các quy trình và đảm bảo rằng chúng tôi không cản trở hiệu suất do chi tiêu quá nhiều . inter-process communication (IPC). Thuộc tính của các quy trìnhcác quy trình UNIX được tạo bởi hệ điều hành và thường chứa các phần sau.
Ưu điểm của quy trình được liệt kê như sau.
Dưới đây là những nhược điểm của quy trình
đa xử lýTrong Python, chúng tôi có thể chọn chạy mã của mình bằng cách sử dụng nhiều luồng hoặc nhiều quy trình nếu chúng tôi muốn thử và cải thiện hiệu suất hơn . Chúng ta có thể sử dụng phương pháp đa luồng và bị giới hạn ở khả năng xử lý của một lõi CPU hoặc ngược lại, chúng ta có thể sử dụng phương pháp đa xử lý và sử dụng toàn bộ số lõi CPU có sẵn trên máy của mình. Trong các máy tính hiện đại ngày nay, chúng ta có xu hướng có nhiều CPU và lõi, do đó, việc giới hạn bản thân chỉ sử dụng một lõi, khiến phần còn lại của máy không hoạt động một cách hiệu quả. Mục tiêu của chúng tôi là cố gắng khai thác toàn bộ tiềm năng từ phần cứng của mình và đảm bảo rằng chúng tôi nhận được giá trị đồng tiền bỏ ra tốt nhất cũng như giải quyết vấn đề của mình nhanh hơn bất kỳ ai khác. Với mô-đun đa xử lý của Python, chúng tôi có thể sử dụng hiệu quả toàn bộ số lõi và CPU, điều này có thể giúp chúng tôi đạt được hiệu suất cao hơn khi gặp sự cố giới hạn CPU. Hình trước cho thấy một ví dụ về cách một lõi CPU bắt đầu ủy thác nhiệm vụ cho các lõi khác Trong tất cả các phiên bản Python nhỏ hơn hoặc bằng 2. 6, chúng ta có thể đạt được số lõi CPU có sẵn cho mình bằng cách sử dụng đoạn mã sau # First we import the multiprocessing module
import multiprocessing
# then we call multiprocessing.cpu_count() which
# returns an integer value of how many available CPUs we have
multiprocessing.cpu_count() Không chỉ đa xử lý cho phép chúng tôi sử dụng máy của mình nhiều hơn mà còn tránh được những hạn chế mà Khóa phiên dịch toàn cầu đặt ra cho chúng tôi . Một nhược điểm tiềm ẩn của nhiều quy trình là chúng ta vốn không có trạng thái chia sẻ và thiếu giao tiếp. Do đó, chúng tôi phải chuyển nó qua một số dạng IPC và hiệu suất có thể bị ảnh hưởng. Tuy nhiên, việc thiếu trạng thái chia sẻ này có thể giúp chúng làm việc dễ dàng hơn vì bạn không phải đấu tranh chống lại các điều kiện chủng tộc tiềm ẩn trong mã của mình Lập trình hướng sự kiệnLập trình hướng sự kiện là một phần quan trọng của cuộc sống của chúng ta--chúng ta thấy các ví dụ của< . Các thiết bị này chạy hoàn toàn theo cách hướng sự kiện; . it every day when we open up our phone, or work on our computer. These devices run purely in an event-driven way; for example, when you click on an icon on your desktop, the operating system registers this as an event, and then performs the necessary action tied to that specific style of event. Mọi tương tác chúng ta thực hiện có thể được mô tả như một sự kiện hoặc một chuỗi sự kiện và những sự kiện này thường kích hoạt các cuộc gọi lại. Nếu bạn đã từng có bất kỳ kinh nghiệm nào với JavaScript, thì bạn sẽ phần nào quen thuộc với khái niệm gọi lại này và mẫu thiết kế gọi lại. Trong JavaScript, trường hợp sử dụng chủ yếu cho các cuộc gọi lại là khi bạn thực hiện các yêu cầu HTTP RESTful và muốn có thể thực hiện một hành động khi bạn biết rằng hành động này đã hoàn tất thành công và chúng tôi đã nhận được phản hồi HTTP của mình Nếu chúng ta nhìn vào hình ảnh trước đó, nó sẽ cho chúng ta thấy một ví dụ về cách các chương trình hướng sự kiện xử lý các sự kiện. Chúng tôi có Trình phát sự kiện ở phía bên trái; . Events, which are picked up by our program's Event Loop, and, should they match a predefined Event Handler, that handler is then fired to deal with the said event. Gọi lại thường được sử dụng trong các tình huống trong đó một hành động không đồng bộ. Ví dụ: giả sử bạn đã nộp đơn xin việc tại Google, bạn sẽ cung cấp cho họ địa chỉ email và sau đó họ sẽ liên hệ với bạn khi họ quyết định. Về cơ bản, điều này giống như đăng ký gọi lại ngoại trừ việc thay vì yêu cầu họ gửi email cho bạn, bạn sẽ thực thi một đoạn mã tùy ý bất cứ khi nào gọi lại Con rùaTurtle là một mô-đun đồ họa đã được viết bằng Python và là một điểm khởi đầu đáng kinh ngạc . Nó xử lý tất cả sự phức tạp đi kèm với lập trình đồ họa và cho phép họ tập trung hoàn toàn vào việc học những điều cơ bản nhất trong khi vẫn khiến họ hứng thú. getting kids interested in programming. It handles all the complexities that come with graphics programming, and lets them focus purely on learning the very basics whilst keeping them interested. Đây cũng là một công cụ rất tốt để sử dụng để để trình diễn các chương trình hướng sự kiện. Nó có tính năng xử lý sự kiện và trình lắng nghe, đó là tất cả những gì chúng ta cần. import turtle
turtle.setup(500,500)
window = turtle.Screen()
window.title("Event Handling 101")
window.bgcolor("lightblue")
nathan = turtle.Turtle()
def moveForward():
nathan.forward(50)
def moveLeft():
nathan.left(30)
def moveRight():
nathan.right(30)
def start():
window.onkey(moveForward, "Up")
window.onkey(moveLeft, "Left")
window.onkey(moveRight, "Right")
window.listen()
window.mainloop()
if __name__ == '__main__':
start() Phá vỡ nóTrong dòng đầu tiên của mẫu mã trước này, chúng tôi nhập mô-đun đồ họa rùa . Sau đó, chúng tôi tiếp tục thiết lập cửa sổ rùa cơ bản với tiêu đề Xử lý sự kiện 101 và màu nền là xanh lam nhạt. Sau khi thiết lập xong ban đầu, chúng ta sẽ tiếp tục xác định ba trình xử lý sự kiện riêng biệt
Khi chúng tôi đã xác định ba trình xử lý riêng biệt của mình, chúng tôi sẽ tiếp tục ánh xạ các trình xử lý sự kiện này tới các lần nhấn phím lên, trái và phải bằng cách sử dụng phương thức Bây giờ chúng tôi đã thiết lập trình xử lý của mình, sau đó chúng tôi yêu cầu họ bắt đầu lắng nghe. Nếu bất kỳ phím nào được nhấn sau khi chương trình của chúng tôi bắt đầu nghe, thì chúng tôi sẽ kích hoạt chức năng xử lý sự kiện của nó. Cuối cùng, khi bạn chạy mã trước đó, bạn sẽ thấy một cửa sổ xuất hiện với một mũi tên ở giữa, bạn có thể di chuyển cửa sổ này bằng các phím mũi tên của mình lập trình phản ứngLập trình phản ứng rất giống với lập trình hướng sự kiện, nhưng thay vì xoay quanh các sự kiện, nó tập trung vào dữ liệu. Cụ thể hơn, nó xử lý các luồng dữ liệu và phản ứng với các thay đổi dữ liệu cụ thể. ReacX - RxPyRxPy tương đương với Python của khung ReactiveX rất phổ biến . Nếu bạn đã từng thực hiện bất kỳ chương trình nào trong Angular 2 và các phiên bản tiếp theo, thì bạn sẽ sử dụng chương trình này khi tương tác với các dịch vụ HTTP. Khung này là sự kết hợp của mẫu quan sát, mẫu iterator và lập trình chức năng. Về cơ bản, chúng tôi đăng ký các luồng dữ liệu đến khác nhau, sau đó tạo các trình quan sát lắng nghe các sự kiện cụ thể đang được kích hoạt. Khi các trình quan sát này được kích hoạt, chúng sẽ chạy mã tương ứng với những gì vừa xảy ra. Chúng tôi sẽ lấy một trung tâm dữ liệu làm ví dụ điển hình về cách lập trình phản ứng có thể được sử dụng. Hãy tưởng tượng trung tâm dữ liệu này có hàng nghìn giá đỡ máy chủ, tất cả đều liên tục tính toán hàng triệu phép tính. Một trong những thách thức lớn nhất trong các trung tâm dữ liệu này là giữ cho tất cả các giá đỡ máy chủ được đóng gói chặt chẽ này đủ mát để chúng không tự làm hỏng. Chúng tôi có thể thiết lập nhiều nhiệt kế trong trung tâm dữ liệu của mình để đảm bảo rằng chúng tôi không bị quá nóng ở bất kỳ đâu và gửi kết quả đọc từ các nhiệt kế này đến máy tính trung tâm dưới dạng một luồng liên tục. Trong trạm điều khiển trung tâm của chúng tôi, chúng tôi có thể thiết lập chương trình RxPy để quan sát luồng thông tin nhiệt độ liên tục này. Trong những người quan sát này, sau đó chúng tôi có thể xác định một loạt các sự kiện có điều kiện để lắng nghe và sau đó phản ứng bất cứ khi nào một trong những điều kiện này bị tấn công Một ví dụ như vậy sẽ là một sự kiện chỉ kích hoạt nếu nhiệt độ của một phần cụ thể của trung tâm dữ liệu trở nên quá ấm. Khi sự kiện này được kích hoạt, thì chúng tôi có thể tự động phản ứng và tăng lưu lượng của bất kỳ hệ thống làm mát nào đến khu vực cụ thể đó và do đó giảm nhiệt độ trở lại. import rx
from rx import Observable, Observer
# Here we define our custom observer which
# contains an on_next method, an on_error method
# and an on_completed method
class temperatureObserver(Observer):
# Every time we receive a temperature reading
# this method is called
def on_next(self, x):
print("Temperature is: %s degrees centigrade" % x)
if (x > 6):
print("Warning: Temperate Is Exceeding Recommended Limit")
if (x == 9):
print("DataCenter is shutting down. Temperature is too high")
# if we were to receive an error message
# we would handle it here
def on_error(self, e):
print("Error: %s" % e)
# This is called when the stream is finished
def on_completed(self):
print("All Temps Read")
# Publish some fake temperature readings
xs = Observable.from_iterable(range(10))
# subscribe to these temperature readings
d = xs.subscribe(temperatureObserver()) Phá vỡ nóHai dòng mã đầu tiên của chúng tôi nhập mô-đun Sau đó, chúng tôi tiếp tục tạo một lớp
Trong hàm Sau khi khai báo lớp, chúng tôi tiếp tục tạo một observable giả chứa 10 giá trị riêng biệt bằng cách sử dụng lập trình GPUGPU nổi tiếng với khả năng hiển thị trò chơi điện tử hành động độ phân giải cao, nhanh chóng. Chúng có thể kết hợp hàng triệu phép tính cần thiết mỗi giây để đảm bảo rằng mọi đỉnh của mô hình 3D trong trò chơi của bạn đều ở đúng vị trí và chúng được cập nhật vài mili giây một lần để đảm bảo tốc độ 60 FPS mượt mà. . Chúng có thể kết hợp hàng triệu phép tính cần thiết mỗi giây để đảm bảo rằng mọi đỉnh của mô hình 3D trong trò chơi của bạn đều ở đúng vị trí và chúng được cập nhật vài mili giây một lần để đảm bảo tốc độ 60 FPS mượt mà. . Chúng có thể kết hợp hàng triệu phép tính cần thiết mỗi giây để đảm bảo rằng mọi đỉnh của mô hình 3D trong trò chơi của bạn đều ở đúng vị trí và chúng được cập nhật vài mili giây một lần để đảm bảo tốc độ 60 FPS mượt mà. . Chúng có thể kết hợp hàng triệu phép tính cần thiết mỗi giây để đảm bảo rằng mọi đỉnh của mô hình 3D trong trò chơi của bạn đều ở đúng vị trí và chúng được cập nhật vài mili giây một lần để đảm bảo tốc độ 60 FPS mượt mà. . Chúng có thể kết hợp hàng triệu phép tính cần thiết mỗi giây để đảm bảo rằng mọi đỉnh của mô hình 3D trong trò chơi của bạn đều ở đúng vị trí và chúng được cập nhật vài mili giây một lần để đảm bảo tốc độ 60 FPS mượt mà. . Chúng có thể kết hợp hàng triệu phép tính cần thiết mỗi giây để đảm bảo rằng mọi đỉnh của mô hình 3D trong trò chơi của bạn đều ở đúng vị trí và chúng được cập nhật vài mili giây một lần để đảm bảo tốc độ 60 FPS mượt mà. . Chúng có thể kết hợp hàng triệu phép tính cần thiết mỗi giây để đảm bảo rằng mọi đỉnh của mô hình 3D trong trò chơi của bạn đều ở đúng vị trí và chúng được cập nhật vài mili giây một lần để đảm bảo tốc độ 60 FPS mượt mà. . Chúng có thể kết hợp hàng triệu phép tính cần thiết mỗi giây để đảm bảo rằng mọi đỉnh của mô hình 3D trong trò chơi của bạn đều ở đúng vị trí và chúng được cập nhật vài mili giây một lần để đảm bảo tốc độ 60 FPS mượt mà. . Chúng có thể kết hợp hàng triệu phép tính cần thiết mỗi giây để đảm bảo rằng mọi đỉnh của mô hình 3D trong trò chơi của bạn đều ở đúng vị trí và chúng được cập nhật vài mili giây một lần để đảm bảo tốc độ 60 FPS mượt mà. . Chúng có thể kết hợp hàng triệu phép tính cần thiết mỗi giây để đảm bảo rằng mọi đỉnh của mô hình 3D trong trò chơi của bạn đều ở đúng vị trí và chúng được cập nhật vài mili giây một lần để đảm bảo tốc độ 60 FPS mượt mà. . Chúng có thể kết hợp hàng triệu phép tính cần thiết mỗi giây để đảm bảo rằng mọi đỉnh của mô hình 3D trong trò chơi của bạn đều ở đúng vị trí và chúng được cập nhật vài mili giây một lần để đảm bảo tốc độ 60 FPS mượt mà. . Chúng có thể kết hợp hàng triệu phép tính cần thiết mỗi giây để đảm bảo rằng mọi đỉnh của mô hình 3D trong trò chơi của bạn đều ở đúng vị trí và chúng được cập nhật vài mili giây một lần để đảm bảo tốc độ 60 FPS mượt mà. . Chúng có thể kết hợp hàng triệu phép tính cần thiết mỗi giây để đảm bảo rằng mọi đỉnh của mô hình 3D trong trò chơi của bạn đều ở đúng vị trí và chúng được cập nhật vài mili giây một lần để đảm bảo tốc độ 60 FPS mượt mà. . Chúng có thể kết hợp hàng triệu phép tính cần thiết mỗi giây để đảm bảo rằng mọi đỉnh của mô hình 3D trong trò chơi của bạn đều ở đúng vị trí và chúng được cập nhật vài mili giây một lần để đảm bảo tốc độ 60 FPS mượt mà. . Chúng có thể kết hợp hàng triệu phép tính cần thiết mỗi giây để đảm bảo rằng mọi đỉnh của mô hình 3D trong trò chơi của bạn đều ở đúng vị trí và chúng được cập nhật vài mili giây một lần để đảm bảo tốc độ 60 FPS mượt mà. . Chúng có thể kết hợp hàng triệu phép tính cần thiết mỗi giây để đảm bảo rằng mọi đỉnh của mô hình 3D trong trò chơi của bạn đều ở đúng vị trí và chúng được cập nhật vài mili giây một lần để đảm bảo tốc độ 60 FPS mượt mà. . Chúng có thể kết hợp hàng triệu phép tính cần thiết mỗi giây để đảm bảo rằng mọi đỉnh của mô hình 3D trong trò chơi của bạn đều ở đúng vị trí và chúng được cập nhật vài mili giây một lần để đảm bảo tốc độ 60 FPS mượt mà. . Chúng có thể kết hợp hàng triệu phép tính cần thiết mỗi giây để đảm bảo rằng mọi đỉnh của mô hình 3D trong trò chơi của bạn đều ở đúng vị trí và chúng được cập nhật vài mili giây một lần để đảm bảo tốc độ 60 FPS mượt mà. . Chúng có thể kết hợp hàng triệu phép tính cần thiết mỗi giây để đảm bảo rằng mọi đỉnh của mô hình 3D trong trò chơi của bạn đều ở đúng vị trí và chúng được cập nhật vài mili giây một lần để đảm bảo tốc độ 60 FPS mượt mà. Nói chung, GPU cực kỳ giỏi trong việc thực hiện song song cùng một tác vụ, hàng triệu triệu lần mỗi phút. Nhưng nếu GPU hoạt động hiệu quả như vậy thì tại sao chúng ta không sử dụng chúng thay vì CPU? . CPU có ít lõi hơn, được thiết kế đặc biệt cho tốc độ khi chuyển ngữ cảnh giữa các tác vụ đang vận hành. Nếu các GPU được giao các nhiệm vụ giống nhau, bạn sẽ thấy hiệu suất tổng thể của máy tính bị suy giảm đáng kể Nhưng làm thế nào chúng ta có thể sử dụng những card đồ họa mạnh mẽ này cho một thứ khác ngoài lập trình đồ họa? . Các thư viện này cố gắng trừu tượng hóa mã cấp thấp phức tạp mà API đồ họa phải tương tác để sử dụng GPU. Chúng giúp chúng tôi tái sử dụng hàng nghìn lõi xử lý nhỏ hơn có sẵn trên GPU và sử dụng chúng cho các chương trình tốn kém về mặt tính toán của chúng tôi đơn giản hơn rất nhiều Những Đơn vị xử lý đồ họa ( GPU ) gói gọn mọi thứ . Chúng có khả năng song song hóa cao và được xây dựng để đạt thông lượng tối đa. Bằng cách sử dụng những thứ này trong Python, chúng tôi có thể tận dụng tối đa cả hai thế giới. Chúng tôi có thể sử dụng một ngôn ngữ được hàng triệu người ưa chuộng do tính dễ sử dụng của ngôn ngữ này, đồng thời làm cho các chương trình của chúng tôi hoạt động hiệu quả đến không ngờ. that scripting languages are not. They are highly parallelizable, and built for maximum throughput. By utilizing these in Python, we are able to get the best of both worlds. We can utilize a language that is favored by millions due to its ease of use, and also make our programs incredibly performant. Trong các phần sau, chúng ta sẽ xem xét các thư viện khác nhau có sẵn cho chúng ta, những thư viện này cho thấy sức mạnh của GPU PyCUDAPyCUDA cho phép chúng tôi tương tác với API tính toán song song CUDA của Nvidia bằng Python. Nó cung cấp cho chúng tôi rất nhiều lợi thế khác nhau so với các khung khác sử dụng cùng một API CUDA cơ bản. Những lợi thế này bao gồm những thứ như tốc độ cơ bản ấn tượng, kiểm soát hoàn toàn API trình điều khiển của CUDA và quan trọng nhất là rất nhiều tài liệu hữu ích để giúp những người mới bắt đầu sử dụng nó. Tuy nhiên, thật không may, hạn chế chính của PyCUDA là nó sử dụng API dành riêng cho Nvidia và do đó, nếu bạn không có cạc đồ họa dựa trên Nvidia, thì bạn sẽ không thể tận dụng lợi thế của nó. Tuy nhiên, có những lựa chọn thay thế khác hoạt động tốt như nhau trên các card đồ họa không phải của Nvidia khác OpenCLOpenCL là một ví dụ về an thay thế cho PyCUDA và trên thực tế, tôi muốn giới thiệu điều này thay vì PyCUDA do phạm vi ấn tượng của nó . OpenCL ban đầu được hình thành bởi Apple và cho phép chúng tôi tận dụng một số nền tảng không đồng nhất như CPU, GPU, bộ xử lý tín hiệu số, mảng cổng lập trình trường và các loại bộ xử lý và bộ tăng tốc phần cứng khác. does also include Nvidia. OpenCL was originally conceived by Apple, and allows us to take advantage of a number of heterogeneous platforms such as CPUs, GPUs, digital signal processors, field-programmable gate arrays, and other different types of processors and hardware accelerators. Hiện tại có các API của bên thứ ba không chỉ cho Python mà còn cho Java và. NET, và do đó, nó lý tưởng cho các nhà nghiên cứu và những người trong chúng ta, những người muốn sử dụng toàn bộ sức mạnh của máy tính để bàn của mình theanoTheano là một ví dụ khác về thư viện cho phép bạn sử dụng GPU cũng như . speeds that rival C implementations when trying to solve problems that involve huge quantities of data. Tuy nhiên, đó là một phong cách lập trình khác, theo nghĩa Python là phương tiện trong đó bạn tạo các biểu thức có thể được chuyển vào . Ghi chúTrang web chính thức của Theano có thể được tìm thấy ở đây. http. //học kĩ càng. net/phần mềm/theano/ Hạn chế của PythonĐầu chương, tôi đã nói về về những hạn chế của GIL or the Global Interpreter Lock that is present within Python, but what does this actually mean? Đầu tiên, tôi nghĩ điều quan trọng là phải biết chính xác GIL làm gì cho chúng ta. GIL về cơ bản là một khóa loại trừ tương hỗ để ngăn nhiều luồng thực thi mã Python song song. Đó là một khóa chỉ có thể được giữ bởi một luồng tại một thời điểm và nếu bạn muốn một luồng thực thi mã của chính nó, thì trước tiên nó phải có được khóa trước khi có thể tiến hành thực thi mã của chính nó. Ưu điểm mà điều này mang lại cho chúng tôi là trong khi nó bị khóa, không có gì khác có thể chạy cùng lúc. Trong sơ đồ trước, chúng ta thấy một ví dụ về cách nhiều luồng bị cản trở bởi GIL này. Mỗi luồng phải đợi và lấy GIL trước khi có thể tiến xa hơn, sau đó giải phóng GIL, thường là trước khi nó có cơ hội hoàn thành công việc của mình. Nó tuân theo cách tiếp cận vòng tròn ngẫu nhiên và bạn không có gì đảm bảo về việc luồng nào sẽ nhận được khóa trước Tại sao điều này là cần thiết, bạn có thể hỏi? . Nhưng nó đã được triển khai với mục đích tốt và để chống lại việc quản lý bộ nhớ Python an toàn không theo luồng. Nó ngăn chúng ta tận dụng các hệ thống đa bộ xử lý trong một số tình huống nhất định Guido Van Rossum, người tạo ra Python, đã đăng một bản cập nhật về việc xóa GIL và các lợi ích của nó trong một bài đăng tại đây. http. //www. nghệ thuật. com/weblog/viewpost. jsp?thread=214235. Anh ấy tuyên bố rằng anh ấy sẽ không chống lại ai đó tạo ra một nhánh Python không có GIL và anh ấy sẽ chấp nhận hợp nhất mã này nếu, . Trước đây đã có những nỗ lực loại bỏ GIL, nhưng người ta nhận thấy rằng việc bổ sung tất cả các khóa bổ sung để đảm bảo an toàn cho luồng thực sự đã làm chậm một ứng dụng hơn hai lần. Nói cách khác, bạn sẽ có thể hoàn thành nhiều công việc hơn với một CPU so với chỉ với hơn hai CPU. Tuy nhiên, có những thư viện như NumPy có thể làm mọi thứ họ cần mà không cần phải tương tác với GIL và hoạt động hoàn toàn bên ngoài GIL là điều tôi sẽ khám phá sâu hơn trong các chương sau của cuốn sách này Cũng cần lưu ý rằng có những triển khai Python khác, chẳng hạn như Jython và IronPython, không có bất kỳ hình thức Khóa phiên dịch toàn cầu nào và như vậy có thể khai thác triệt để các hệ thống đa bộ xử lý. Jython và IronPython đều chạy trên các máy ảo khác nhau, vì vậy, chúng có thể tận dụng môi trường thời gian chạy tương ứng của chúng JythonJython là một triển khai của Python hoạt động trực tiếp với nền tảng Java. Nó có thể được sử dụng theo kiểu bổ sung với Java dưới dạng ngôn ngữ kịch bản và đã được chứng minh là vượt trội so với CPython, đó là the standard implementation of Python, when working with some large datasets. For the majority of stuff though, CPython's single-core execution typically outperforms Jython and its multicore approach. Lợi thế khi sử dụng Jython là bạn có thể thực hiện một số điều thú vị với nó khi làm việc trong Java, chẳng hạn như nhập các thư viện và khung công tác Java hiện có và sử dụng chúng như thể chúng là một phần của mã Python của bạn Trăn sắtIronPython là. NET tương đương với Jython và hoạt động trên top của Microsoft. Nền tảng NET. Một lần nữa, bạn sẽ có thể sử dụng nó theo cách bổ sung với. ứng dụng NET. Điều này phần nào có lợi cho. NET, vì họ có thể sử dụng Python như một ngôn ngữ kịch bản nhanh và biểu cảm trong. ứng dụng NET. Tại sao chúng ta nên sử dụng Python?Nếu Python có những hạn chế rõ ràng, đã biết như vậy khi nói đến việc viết các ứng dụng đồng thời, có hiệu suất cao, thì tại sao chúng ta lại tiếp tục sử dụng nó? . Đó là một ngôn ngữ trực quan, dễ tiếp thu và hiểu đối với những người không nhất thiết phải có nhiều kinh nghiệm lập trình. Ngôn ngữ này đã nhận được tỷ lệ chấp nhận rất lớn giữa các nhà khoa học dữ liệu và nhà toán học làm việc trong các lĩnh vực cực kỳ thú vị như học máy và phân tích định lượng, những người nhận thấy ngôn ngữ này là một công cụ cực kỳ hữu ích trong kho vũ khí của họ Trong cả hệ sinh thái Python 2 và 3, bạn sẽ tìm thấy một số lượng lớn thư viện được thiết kế dành riêng cho các trường hợp sử dụng này và bằng cách biết về các hạn chế của Python, chúng tôi có thể giảm thiểu chúng một cách hiệu quả và tạo ra phần mềm hiệu quả và có khả năng thực hiện Vì vậy, bây giờ chúng ta đã hiểu luồng và quy trình là gì, cũng như một số hạn chế của Python, đã đến lúc xem xét cách chúng ta có thể sử dụng đa luồng trong ứng dụng của mình để cải thiện tốc độ của chương trình Tải xuống hình ảnh đồng thờiKhông còn nghi ngờ gì nữa, một ví dụ tuyệt vời về lợi ích của đa luồng là việc sử dụng nhiều luồng để tải xuống nhiều hình ảnh hoặc tệp. Trên thực tế, đây là một trong những trường hợp sử dụng tốt nhất cho đa luồng do tính chất chặn của I/O. Để làm nổi bật mức tăng hiệu suất, chúng tôi sẽ truy xuất 10 hình ảnh khác nhau từ http. // lorempixel. com/400/200/sports, đây là một API miễn phí cung cấp một hình ảnh khác mỗi khi bạn nhấn vào liên kết đó. Sau đó, chúng tôi sẽ lưu trữ 10 hình ảnh khác nhau này trong một thư mục tạm thời để chúng tôi có thể xem/sử dụng chúng sau này Tất cả mã được sử dụng trong các ví dụ này có thể được tìm thấy trong kho lưu trữ GitHub của tôi tại đây. https. //github. com/elliotforbes/Concurrency-With-Python . Tải xuống tuần tựĐầu tiên, chúng ta nên có một số mẫu của đường cơ sở mà chúng ta có thể measure the performance gains. To do this, we'll write a quick program that will tải xuống 10 hình ảnh này liên tục như sau. import urllib.request
def downloadImage(imagePath, fileName):
print("Downloading Image from ", imagePath)
urllib.request.urlretrieve(imagePath, fileName)
def main():
for i in range(10):
imageName = "temp/image-" + str(i) + ".jpg"
downloadImage("http://lorempixel.com/400/200/sports", imageName)
if __name__ == '__main__':
main() Phá vỡ nóTrong mã trước, chúng tôi bắt đầu bằng cách nhập Trong hàm Khi chạy tập lệnh này, bạn sẽ thấy thư mục tải xuống đồng thờiBây giờ chúng tôi đã có cơ sở của mình, đã đến lúc viết một chương trình nhanh rằng sẽ đồng thời tải xuống tất cả các hình ảnh mà chúng tôi yêu cầu. Chúng ta sẽ tiếp tục tạo và bắt đầu chủ đề trong các chương sau, vì vậy đừng lo lắng nếu bạn gặp khó khăn trong việc hiểu mã. Điểm mấu chốt của việc này là nhận ra hiệu suất tiềm năng đạt được khi viết chương trình đồng thời. import threading
import urllib.request
import time
def downloadImage(imagePath, fileName):
print("Downloading Image from ", imagePath)
urllib.request.urlretrieve(imagePath, fileName)
print("Completed Download")
def executeThread(i):
imageName = "temp/image-" + str(i) + ".jpg"
downloadImage("http://lorempixel.com/400/200/sports", imageName)
def main():
t0 = time.time()
# create an array which will store a reference to
# all of our threads
threads = []
# create 10 threads, append them to our array of threads
# and start them off
for i in range(10):
thread = threading.Thread(target=executeThread, args=(i,))
threads.append(thread)
thread.start()
# ensure that all the threads in our array have completed
# their execution before we log the total time to complete
for i in threads:
i.join()
# calculate the total execution time
t1 = time.time()
totalTime = t1 - t0
print("Total Execution Time {}".format(totalTime))
if __name__ == '__main__':
main() Phá vỡ nóTrong dòng đầu tiên của chương trình mới được sửa đổi của chúng tôi, bạn sẽ thấy rằng chúng tôi hiện đang nhập mô-đun phân luồng; . Sau đó, chúng tôi trừu tượng hóa việc tạo tên tệp của chúng tôi và gọi hàm Trong hàm Cuối cùng, chúng tôi lặp qua mảng luồng của mình bằng cách gọi i trong luồng và gọi phương thức Nếu bạn thực hiện điều này trên máy của mình, bạn sẽ thấy rằng nó gần như bắt đầu tải xuống 10 hình ảnh khác nhau ngay lập tức. Khi quá trình tải xuống hoàn tất, nó lại in ra thông báo rằng quá trình tải xuống đã hoàn tất thành công và bạn sẽ thấy thư mục Cả hai tập lệnh trước đều thực hiện chính xác các tác vụ giống nhau bằng cách sử dụng chính xác cùng một thư viện Cải thiện xử lý số bằng đa xử lýVì vậy, chúng tôi đã biết chính xác cách chúng tôi có thể cải thiện những thứ như tải xuống hình ảnh, nhưng làm thế nào để chúng tôi cải thiện hiệu suất của số của chúng tôi . Trong ví dụ này, chúng ta sẽ cố gắng tìm các thừa số nguyên tố của 10.000 số ngẫu nhiên nằm trong khoảng từ 20.000 đến 100.000.000. Chúng tôi không nhất thiết phải lo lắng về thứ tự thực hiện miễn là công việc được hoàn thành và chúng tôi không chia sẻ bộ nhớ giữa bất kỳ quy trình nào của chúng tôi Thừa số nguyên tố tuần tựMột lần nữa, chúng ta sẽ viết một tập lệnh thực hiện điều này theo cách tuần tự mà chúng ta . easily verify is working correctly: import time
import random
def calculatePrimeFactors(n):
primfac = []
d = 2
while d*d <= n:
while (n % d) == 0:
primfac.append(d)# supposing you want multiple factors repeated
n //= d
d += 1
if n > 1:
primfac.append(n)
return primfac
def main():
print("Starting number crunching")
t0 = time.time()
for i in range(10000):
rand = random.randint(20000, 100000000)
print(calculatePrimeFactors(rand))
t1 = time.time()
totalTime = t1 - t0
print("Execution Time: {}".format(totalTime))
if __name__ == '__main__':
main() Phá vỡ nóHai dòng đầu tiên tạo nên các lần nhập bắt buộc của chúng tôi--chúng tôi sẽ cần cả thời gian và các mô-đun ngẫu nhiên. Sau khi nhập xong, chúng ta tiếp tục xác định hàm Sau đó, chúng tôi xác định hàm Nếu bạn thực hiện điều này trên máy tính của mình, bạn sẽ thấy mảng các thừa số nguyên tố được in ra cho 10.000 số ngẫu nhiên khác nhau, cũng như tổng thời gian thực hiện cho mã này. Đối với tôi, nó mất khoảng 3. 6 giây để thực hiện trên Macbook của tôi Thừa số nguyên tố đồng thờiVì vậy, bây giờ chúng ta hãy xem cách chúng tôi có thể cải thiện hiệu suất của chương trình này bằng cách sử dụng nhiều quy trình. Để chia nhỏ khối lượng công việc này, chúng tôi sẽ xác định một hàm import time
import random
from multiprocessing import Process
# This does all of our prime factorization on a given number 'n'
def calculatePrimeFactors(n):
primfac = []
d = 2
while d*d <= n:
while (n % d) == 0:
primfac.append(d)# supposing you want multiple factors repeated
n //= d
d += 1
if n > 1:
primfac.append(n)
return primfac
# We split our workload from one batch of 10,000 calculations
# into 10 batches of 1,000 calculations
def executeProc():
for i in range(1000):
rand = random.randint(20000, 100000000)
print(calculatePrimeFactors(rand))
def main():
print("Starting number crunching")
t0 = time.time()
procs = []
# Here we create our processes and kick them off
for i in range(10):
proc = Process(target=executeProc, args=())
procs.append(proc)
proc.start()
# Again we use the .join() method in order to wait for
# execution to finish for all of our processes
for proc in procs:
proc.join()
t1 = time.time()
totalTime = t1 - t0
# we print out the total execution time for our 10
# procs.
print("Execution Time: {}".format(totalTime))
if __name__ == '__main__':
main() Phá vỡ nóMã cuối cùng này thực hiện chính xác chức năng như mã được đăng ban đầu của chúng tôi. Tuy nhiên, thay đổi đầu tiên là ở dòng ba. Ở đây, chúng tôi nhập quy trình từ mô-đun đa xử lý. Sau đây của chúng tôi, phương pháp Sau đó, bạn sẽ thấy rằng chúng tôi đã rút ra vòng lặp Trong hàm Sau khi chúng tôi đã tạo 10 quy trình riêng lẻ, sau đó chúng tôi sẽ chuyển qua các quy trình này hiện có trong mảng Nếu bạn thực hiện điều này ngay bây giờ, bạn sẽ thấy 10.000 đầu ra hiện được in ra trong bảng điều khiển của mình và bạn cũng sẽ thấy thời gian thực hiện thấp hơn nhiều so với thực thi tuần tự của mình. Để tham khảo, chương trình tuần tự được thực hiện trong 3. 9 giây trên máy tính của tôi so với 1. 9 giây khi chạy phiên bản đa xử lý Đây chỉ là minh họa rất cơ bản về cách chúng ta có thể triển khai đa xử lý vào các ứng dụng của mình. Trong các chương sau, chúng ta sẽ khám phá cách chúng ta có thể tạo nhóm và sử dụng bộ thực thi. Điểm mấu chốt cần rút ra từ điều này là chúng ta có thể cải thiện hiệu suất của một số tác vụ liên quan đến CPU bằng cách sử dụng nhiều lõi Bản tóm tắtBây giờ, bạn đã hiểu rõ một số khái niệm cơ bản làm nền tảng cho lập trình đồng thời. Bạn nên nắm bắt các luồng, quy trình và bạn cũng sẽ biết một số hạn chế và thách thức của Python khi triển khai các ứng dụng đồng thời của riêng bạn. Cuối cùng, bạn cũng đã tận mắt chứng kiến một số cải tiến về hiệu suất mà bạn có thể đạt được nếu bạn thêm các loại đồng thời khác nhau vào ứng dụng của mình Bây giờ tôi phải làm rõ rằng không có viên đạn bạc nào mà bạn có thể áp dụng cho mọi ứng dụng và nhận thấy những cải thiện hiệu suất nhất quán. Một kiểu lập trình đồng thời có thể hoạt động tốt hơn kiểu khác tùy thuộc vào yêu cầu của ứng dụng của bạn, vì vậy trong vài chương tiếp theo, chúng ta sẽ xem xét tất cả các cơ chế khác nhau mà bạn có thể sử dụng và thời điểm sử dụng chúng Trong chương tiếp theo, chúng ta sẽ có cái nhìn sâu hơn về khái niệm đồng quy và song song, cũng như sự khác biệt giữa hai khái niệm này. Chúng ta cũng sẽ xem xét một số tắc nghẽn chính hạn chế các hệ thống đồng thời của chúng ta và bạn sẽ tìm hiểu các kiểu kiến trúc hệ thống máy tính khác nhau cũng như cách nó có thể giúp chúng ta đạt được hiệu suất cao hơn Python có tốt cho đồng thời không?Python cung cấp cơ chế cho cả đồng thời và song song , mỗi cơ chế có cú pháp và trường hợp sử dụng riêng. Python có hai cơ chế khác nhau để triển khai đồng thời, mặc dù chúng chia sẻ nhiều thành phần chung. Đây là luồng và coroutines hoặc không đồng bộ.
Đồng thời hoạt động như thế nào trong Python?Định nghĩa đồng thời trong từ điển là sự xuất hiện đồng thời. Trong Python, những thứ xảy ra đồng thời được gọi bằng các tên khác nhau (luồng, tác vụ, quy trình) nhưng ở cấp độ cao, chúng đều đề cập đến một chuỗi các lệnh chạy theo thứ tự . . . . . . . . . . . . . . . . . . . . . . . . . . . .
Lộ trình học Python là gì?Mọi thứ từ kiến thức cơ bản tuyệt đối về Python, đến phát triển web và quét web, đến trực quan hóa dữ liệu và hơn thế nữa . Cho dù bạn là một Pythonista mới bắt đầu, trung cấp hay cao cấp, Lộ trình học tập tùy chỉnh của chúng tôi sẽ đưa các kỹ năng của bạn lên một tầm cao mới với một kế hoạch học tập thực hành, cấp tốc.
Đồng thời Python có phải là một luồng không?Python KHÔNG phải là ngôn ngữ đơn luồng . Các quy trình Python thường sử dụng một luồng đơn vì GIL. Bất chấp GIL, các thư viện thực hiện các tác vụ tính toán nặng như numpy, scipy và pytorch sử dụng các triển khai dựa trên C dưới mui xe, cho phép sử dụng nhiều lõi. |