Coder Sơn Trang

Chúng tôi rất vui mừng chia sẻ kiến thức về từ khóa Non blocking la gi để tối ưu hóa nội dung trang web và tiếp thị trực tuyến. Bài viết cung cấp phương pháp tìm kiếm, phân tích từ khóa và chiến lược hiệu quả. Cảm ơn sự quan tâm và hãy tiếp tục theo dõi để cập nhật kiến thức mới.

https://codersontrang.files.wordpress.com/2017/09/codersontrang-com.png

Bạn Đang Xem: Coder Sơn Trang

Trong quá trình học tập và thao tác làm việc tất cả chúng ta chắc hẳn đâu nhé đã và đang từng thấy hay nghe về BlockingNon-Blocking. Nếu các bạn trước đó chưa từng nghe về hai thuật ngữ trên thì hôm nay sau khoản thời gian đọc xong nội dung bài viết này, mình hi vọng rằng các bạn cũng có thể đã sở hữu một ý niệm cũng như hình tượng được ý nghĩa của chúng.

Blocking và Non-Blocking trong lập trình chủ yếu được đề cập khi muốn miêu tả về kiểu cách một Khóa học thực hiện các dòng lệnh của nó. Tất cả chúng ta có thể hiểu một cách đơn giản, nếu Khóa học được thực hiện theo mô hình Blocking có tức là các dòng lệnh được thực hiện một cách tuần tự. Khi một dòng lệnh ở phía trước không được hoàn thành thì những dòng lệnh phía sau sẽ không được thực hiện và phải đợi khi mà thao tác phía trước hoàn thành, và nếu như các dòng lệnh trước là các thao tác cần nhiều thời kì xử lý như liên quan đến IO (input/output) hay mạng (networking) thì bản thân nó sẽ trở thành vật cản trở ( blocker ) cho những lệnh xử lý phía sau mặc dù theo logic thì có những việc ở phía sau ta có thể xử lý được luôn mà không cần thiết phải đợi vì chúng không có liên quan gì đến nhau.

Mô hình blocking tồn tại từ lịch sử vẻ vang, khi mà máy tính chỉ có thể xử lý đơn nhiệm trên một lõi (core) của cục vi xử lý (chip). Nhưng theo thời kì, công nghệ ngày một trưởng thành với những thành tựu về phần cứng, máy tính giờ có thể làm nhiều việc cùng một lúc thì người ta cần phải suy nghĩ đến việc làm thế nào tận dụng được tối đa tài nguyên xử lý của máy tính và tránh lãng phí nó. Từ này mà bất luận nơi nào có phần xử lý Blocking không cấp thiết, người ta cần thay vào trong 1 giải pháp xử dụng tài nguyên khôn ngoan hơn, đó là Non-Blocking.

Trong mô hình Non-Blocking, các dòng lệnh không nhất thiết phải lúc nào thì cũng phải thực hiện một cách tuần tự (sequential) và đồng bộ (synchronous) với nhau. Ở mô hình này nếu như về mặt logic dòng lệnh phía sau không phụ thuộc vào kết quả của dòng lệnh phía trước, thì nó cũng luôn có thể hoàn toàn được thực hiện ngay sau khoản thời gian dòng lệnh phía trước được gọi mà không cần đợi cho tới khi kết quả được sinh ra. Những dòng lệnh phía trước miêu tả ở trên còn tồn tại thể gọi là được thực hiện Theo phong cách không đồng bộ (Asynchronous), và đi theo mỗi dòng lệnh thường có một callback (lời gọi lại) là đoạn mã sẽ tiến hành thực hiện ngay sau khoản thời gian có kết quả trả về từ dòng lệnh không đồng bộ. Để thực hiện mô hình Non-Blocking, người ta có những phương pháp để thực hiện khác nhau, nhưng về cơ bản vẫn dựa vào việc dùng nhiều Thread (luồng) khác nhau trong cùng một Process (tiến trình), hay thậm chí còn nhiều Process khác nhau (inter-process communication – IPC) để thực hiện. Và mẫu thiết kết (design pattern) tên là event-loop là một trong những mẫu thiết kế nổi tiếng để thực hiện cơ chế Non-Blocking mà nếu có xét tuyển trong tương lai mình sẽ viết bài để giới thiệu cho những bạn.

Trong nội dung bài viết này mình sẽ đưa ra một ví dụ để các bạn làm rõ hơn về Blocking và Non-Blocking, gồm có hình ảnh minh họa cũng như Khóa học đơn giản viết bằng Java. Ví dụ này mô tả quá trình lấy tài liệu từ 3 lời gọi hàm khác nhau và sau đó in kết quả khi trả về từ hàm ra màn hình hiển thị. Lời gọi hàm trong ví dụ chỉ là một đoạn code đơn giản mô phỏng một việc làm trong một thời kì nhất định, trong thực tế việc này còn có thể thao tác disk IO như đọc tài liệu từ file hay database, hoặc thao tác liên quan đến kết nối mạng như gọi webservice … 3 hàm ở trên mình giả sử sẽ là 3 việc thực tế không liên quan gì đến nhau, và mình sẽ chỉ ra cùng là làm 3 việc thì cơ chế Blocking sẽ khác với Non-Blocking thế nào.

Trước tiên các bạn hãy nhìn vào hình ảnh minh họa về Blocking và Non-Blocking ở ở chỗ này

Phần phía trên miêu tả sự hoạt động theo cơ chế Blocking mà ở đây mặc dù không có sự liên đới giữa 3 việc, nhưng các công việc tiếp sau luôn phải chờ công việc phía trước thực sự xong rồi mới có thể mở màn thực hiện. Các bước sẽ tiến hành mô tả như ở chỗ này

  1. Hàm dataSync1.get() được gọi để lấy tài liệu, vì nó là Blocking nên trước lúc công việc này hoàn thành các việc tiếp sau sẽ phải đợi
  2. Hàm printData(d1) được gọi để in tài liệu lấy về từ dataSync1.get(), tương tự nó cũng là Blocking
  3. Hàm dataSync2.get() được gọi để lấy tài liệu, mặc dùng là nó không liên quan gì đến hai dòng lệnh trên, nhưng đến tận hiện nay nó mới được thực hiện và là Blocking nên chiếm một khoảng tầm thời kì xử lý nữa
  4. Hàm printData(d2) được gọi để in tài liệu lấy về từ dataSync2.get(), là Blocking
  5. Hàm dataSync3.get() được gọi để lấy tài liệu, là Blocking
  6. Hàm printData(d3) được gọi để in tài liệu lấy về dataSync3.get(), là Blocking

Xem Thêm : Sucrose (saccharose) là gì? Vai trò của sucrose đối với sức khỏe

Ở phần này, mọi thao tác đều là blocking nên thời kì để thực hiện xong hết các thao tác sẽ bằng tổng thời kì của từng thao tác.

Phía dưới là phần thể hiện việc làm tất cả những việc trên, các thao tác in dự liệu printData(d1), printData(d2), printData(d3) vẫn là các thao tác Blocking nhưng ở đây có sự tham gia của Non-Blocking trong các thao tác lấy tài liệu dataAsync1.get(), dataAsync2.get(), dataAsync3.get(). Các thao tác Non-Blocking sẽ tiến hành mở màn gần như ngay tức thời và không cần thiết phải chờ các thao tác phía trước thực hiện xong. Sau khoản thời gian có kết quả các thao tác Non-Blocking sẽ gọi lại callback để in kết quả trả về ra màn hình hiển thị. Cụ thể sẽ tiến hành diễn giải như ở ở chỗ này:

  1. Hàm dataAsync1.get() được gọi để lấy dự liệu, vì nó là Non-Blocking nên quá trình thực thi sẽ không còn phải dừng ở đây mà tiếp tục thực hiện dòng lệnh tiếp sau gần như ngay tức thời, tất nhiên vẫn phải sau khoản thời gian đăng ký một callback để in ra tài liệu trả về từ dataAsync1.get().
  2. Như nói ở trên, ngay sau đó, hàm dataAsync2.get() được gọi cùng với đăng ký callback. Vì là Non-Blocking nên quá trình cũng giống như trên.
  3. Tiếp theo hàm dataAsync3.get() cũng được thực hiện tương tự. Đến đây, 3 hàm gọi để lấy tài liệu gần như được thực hiện song song mà không cần thiết phải chờ nhau.
  4. Trong những lúc hàm dataAsync1.get() và dataAsync3.get() đang thực hiện thì hàm dataAsync2.get() đã lấy được tài liệu về, lúc này callback được gọi để in tài liệu đó ra màn hình hiển thị, trong callback lúc này printData(d2) được gọi và nó là Blocking.
  5. Trong thời kì printData(d2) đang thực hiện, dataAsync1.get() đã hoàn thành việc lấy tài liệu, callback của nó được gọi tuy nhiên vì printData(d2) là Blocking và đang thực hiện nên việc thực hiện printData(d1) sẽ phải chờ.
  6. Cũng tương tự như trên, dataAsync3.get() cũng hoàn thành việc lấy tài liệu, callback của nó được gọi, lần này printData(d3) không những phải chờ printData(d2) như trên mà nó còn phải chờ thêm cả printData(d1) bởi vì printData(d1) cũng là Blocking. Sau khoản thời gian cả printData(d2) và printData(d1) được hoàn thành thì printData(d3) được thực hiện và toàn bộ quá trình hoàn thành.

Thời điểm này nhìn lại hình vẽ một lần nữa ta có thể thấy Non-Blocking rút ngắn thời kì thực hiện Khóa học hơn là Blocking, việc rút ngắn thời kì này sẽ không phải vì các công việc được thực hiện nhanh hơn mà vì nhiều việc được thực hiện cùng một lúc hơn.

Sau đây là đoạn code demo cho việc thực thi với Blocking và Non-Blocking được viết bằng Java. Tất cả chúng ta sẽ tạo một Java project đơn giản trên IntelliJ IDE như hình ở chỗ này

DataSync.java

public class DataSync { private int id; private long simulationDuration; DataSync(int id, long simulationDuration){ this.id = id; this.simulationDuration = simulationDuration; } public String get(){ try { Thread.sleep(this.simulationDuration); } catch (InterruptedException e) { e.printStackTrace(); } return “data-“+id; } }

Lớp DataSync thể hiện nguồn tài liệu có thể lấy về theo cơ chế Blocking, một nguồn tài liệu được mô tả gồm có

  • id : định danh nguồn tài liệu, như ở trên miêu tả, tất cả chúng ta có ba nguồn được định danh là một trong những, 2, 3
  • simulationDuration: tính bằng mili giây, giả lập quãng thời kì cần để lấy được tài liệu về từ nguồn tài liệu qua phương thức get().

MainSync.java

public class MainSync { public static void main(String[] args) { long startTime, endTime; DataSync dataSync1 = new DataSync(1, 5000); //5s DataSync dataSync2 = new DataSync(2, 3000); //3s DataSync dataSync3 = new DataSync(3, 6000); //6s startTime = System.currentTimeMillis(); System.out.println(“Start”); String d1 = dataSync1.get(); printData(d1); String d2 = dataSync2.get(); printData(d2); String d3 = dataSync3.get(); printData(d3); System.out.println(“Done”); endTime = System.currentTimeMillis(); System.out.print(“Execution time (ms): “+(endTime- startTime)); } private static void printData(String data){ try { Thread.sleep(1000); } catch (InterruptedException e) { e.printStackTrace(); } System.out.println(“Synchronously printing “+data); } }

MainSync.java gồm có phương thức main() là điều mở màn của Khóa học. Trước nhất ba nguồn tài liệu Blocking được khởi tạo tuần tự là dataSync1, dataSync2 và dataSync3 với ba giá trị thời kì khác nhau là 5 giây, 3 giây và 6 giây. Như vậy nguồn tài liệu số 2 sẽ thực hiện nhanh nhất rồi đến số 1 và cuối cùng là số 3. Sau đó ở mỗi nguồn tài liệu sẽ tiến hành gọi phương thức get() để lấy dự liệu về theo cơ chế Blocking. Tài liệu sẽ tiến hành in ra ngay sau khoản thời gian được trả về từ nguồn tài liệu qua phương thức printData(). Phương thức printData() cũng là Blocking và ta mô phỏng thời kì để thực hiện công việc này trong quãng thời kì 1 giây. Ở phía cuối ta cũng in khoảng tầm thời kì tính bằng mili giây để toàn bộ Khóa học hoàn thành.

Và khi chạy Khóa học ta thấy được trật tự các câu lệnh được thực hiện giống như mô tả trên hình minh họa phần đầu cũng như tổng thời kì hoàn thành là 17001 mili giây như hình ở chỗ này:

DataAsync.java

Xem Thêm : Địa chỉ học nấu ăn – Làm bánh tại tp HCM

import java.util.function.Supplier; public class DataAsync implements Supplier { private int id; private long simulationDuration; DataAsync(int id, long simulationDuration){ this.id = id; this.simulationDuration = simulationDuration; } @Override public String get() { try{ Thread.sleep(simulationDuration); }catch (Exception e){} return “data-“+id; } }

Lớp DataAsync thể hiện nguồn tài liệu có thể lấy về theo cơ chế Non-Blocking, và tương tự như lớp DataSync phía bên trên, ở đây cũng luôn có id và simulationDuration.

MainAsync.java

import java.util.concurrent.*; public class MainAsync { public static void main(String[] args) { long startTime, endTime; CountDownLatch latch = new CountDownLatch(3); DataAsync dataAsync1 = new DataAsync(1, 5000); DataAsync dataAsync2 = new DataAsync(2, 3000); DataAsync dataAsync3 = new DataAsync(3, 6000); startTime = System.currentTimeMillis(); System.out.println(“Start”); try{ CompletableFuture.supplyAsync(dataAsync1).thenAccept(d1 -> { printData(d1); latch.countDown(); }); CompletableFuture.supplyAsync(dataAsync2).thenAccept(d2 -> { printData(d2); latch.countDown(); }); CompletableFuture.supplyAsync(dataAsync3).thenAccept(d3 -> { printData(d3); latch.countDown(); }); latch.await(); System.out.println(“Done”); endTime = System.currentTimeMillis(); System.out.print(“Execution time (ms): “+(endTime- startTime)); }catch (Exception e){ } } private static void printData(String data){ try { Thread.sleep(1000); } catch (InterruptedException e) { e.printStackTrace(); } System.out.println(“Synchronously printing “+data); } }

Cũng giống như lớp MainSync ở trên, ta cũng khởi tạo 3 nguồn tài liệu nhưng lần này là Non-Blocking, các định danh cũng thời kì giả lập để thực hiện lấy tài liệu về không có gì thay đổi. Tiếp đó để thực hiện việc lấy tài liệu theo cơ chế Non-Blocking, ta sử dụng CompletableFuture của Java 8 để nhận vào nguồn tài liệu qua hàm supplyAsync(). supplyAsync() sẽ thực hiện hàm get() Theo phong cách Non-Blocking từ thông số nguồn vào là một java.util.function.Supplier, chính vì vậy mà ta thấy vì sao lớp DataAsync ở trên phải implement java.util.function.Supplier. Và song song ta cũng luôn có thể đăng ký callback cho từng lời gọi Non-Blocking này qua phương thức thenAccept() mà ở đây cụ thể là in giá trị trả về qua phương thức printData().

Ở đây tất cả chúng ta có một CountDownLatch được sử dụng, bởi vì các lời gọi hàm là Non-Blocking nên các lệnh phía sau sẽ thực hiện mà không cần chờ các lệnh phía trước nó hoàn thành. Chính vì vậy mà khi 3 nguồn tài liệu còn chưa kịp trả về kết quả, thread thực hiện phương thức main() sẽ chạy hết Khóa học trước mà không kịp in các tài liệu trả về qua các callback. Đó đó chính là lý do vì sao tất cả chúng ta sử dụng CountDownLatch ở đây, mục tiêu đó chính là để chờ khi tất cả callback được hoàn thành thì ta mới kết thúc Khóa học.

Khi tiến hành chạy Khóa học, trật tự các kết quả được in ra sẽ giống như mô tả ở hình vẽ tại phần đầu và tổng thời kì thực hiên Khóa học lần này chỉ là 7171 mili giây thay vì 17001 mili giây khi thực hiện với cơ chế Blocking.

Ngày này khi mà các thế hệ phần cứng ngày một trưởng thành với khả năng xử lý song song, thì việc các ứng dụng cần đáp ứng khả năng sử dụng tài nguyên một cách tối ưu là điều rất cấp thiết. Non-Blocking là mô hình mà các ứng dụng sẽ luôn hướng đến mọi lúc có thể. Trong một số tiếng nói truyền thống như Java, mỗi một dòng lệnh đa phần sẽ là Blocking trong Thread gọi nó, các lập trình viên có thể tạo một cơ chế Non-Blocking trong Khóa học của mình bằng việc dữ thế chủ động sử dụng các API để tạo Thread khác, CompletableFuture… hoặc mạnh hơn là lập trình với giao thức Reactive Stream (RxJava). Trong các nền tảng tân tiến ra đời sau như NodeJS, mọi dòng code đa phần sẽ đều là Non-Blocking, giúp cho những lập trình viên dễ dàng hơn rất nhiều trong việc sử dụng tối ưu tài nguyên, tránh lãng phí khi không cấp thiết phải đợi chờ các thao tác xử lý đa phần là liên quan đến IO và Network, cũng như tránh các vấn đề phức tạp lúc các lập trình viên phải tự mình tạo ra và quản lý các luồng xử lý không đồng bộ với nhau.

Cuối cùng hi vọng nội dung bài viết này sẽ giúp một phần nào đó cho những bạn cũng có thể hình dung ra và phân biệt được mô hình lập trình Blocking, Non-Blocking và đã sở hữu sự so sánh giữa chúng.

Good luck!

You May Also Like

About the Author: v1000