Clprolf Docs #5 — Hiểu Rõ Về Đồng Thời và Tính Song Song
Giới thiệu
Trong loạt tài liệu chính thức về Clprolf, bài viết này (phần 5 trong 6) sẽ giúp bạn hiểu rõ hơn về đồng thời và tính song song trong ngôn ngữ lập trình Clprolf. Clprolf không chỉ là một ngôn ngữ mới mà còn là một framework hỗ trợ lập trình hướng đối tượng (OOP) một cách rõ ràng hơn bằng cách áp dụng các vai trò, trách nhiệm và thiết kế sạch.
Các Hỗ Trợ Cho Đồng Thời và Tính Song Song Trong Clprolf
Clprolf giới thiệu một loạt các hỗ trợ ngữ nghĩa nhằm giúp việc đồng thời và tính song song trở nên dễ hiểu hơn và an toàn hơn khi triển khai. Các hỗ trợ này không nhằm thay thế các cấu trúc mạnh mẽ của Java, mà để làm rõ mục đích và đưa chúng lại gần hơn với phương pháp thiết kế rõ ràng của Clprolf.
Chúng chủ yếu áp dụng cho các tác nhân (agents), nhưng cũng có thể áp dụng cho các tác nhân công việc (worker agents), đảm bảo rằng cả mô phỏng thực tế và các thành phần kỹ thuật đều có thể được hưởng lợi.
Hỗ Trợ Đồng Thời
Clprolf cung cấp hai modifier:
long_action(@Long_action)prevent_missing_collision(@Prevent_missing_collision)
Những modifier này giúp làm rõ cách xử lý các hành động mất thời gian và đảm bảo rằng các tương tác quan trọng (như va chạm) không bao giờ bị bỏ lỡ.
Modifier long_action
Một số phương thức trong một tác nhân đại diện cho các hành động mất thời gian, không chỉ là các thay đổi trạng thái ngay lập tức.
Ví dụ: trong một trò chơi điện tử, phương thức Player.jump() là một hành động dài — nhân vật sẽ nhảy lên, rơi xuống và hạ cánh.
Chúng ta không muốn quản lý từng bước một cách thủ công; chúng ta chỉ muốn kích hoạt hành động. Clprolf chia hành động thành các bước, tự động thực hiện cho đến khi hoàn thành.
Modifier prevent_missing_collision
Các va chạm có thể bị bỏ lỡ nếu hai thực thể di chuyển cùng lúc và đi xuyên qua nhau. Để tránh điều này, các phương thức cập nhật vị trí được đánh dấu với prevent_missing_collision.
Điều này đảm bảo rằng kiểm tra va chạm được thực hiện trước khi chuyển động được áp dụng.
Hỗ Trợ Tính Song Song
Clprolf bổ sung cho các cấu trúc của Java với ba modifier cho việc thực thi song song:
one_at_a_time(@One_at_a_time)turn_monitor(@Turn_monitor)for_every_thread(@For_every_thread)
Những modifier này không thay đổi cơ chế bên dưới, nhưng làm cho việc đa luồng trở nên rõ ràng và dễ đọc hơn.
Modifier one_at_a_time
Đánh dấu một phương thức là đồng bộ.
Điều này làm rõ rằng chỉ có một luồng tại một thời điểm có thể thực thi phương thức này, tạo cảm giác như ứng dụng đang chạy một luồng cho các phần quan trọng.
Modifier turn_monitor
Điều phối nhiều phương thức one_at_a_time giữa các lớp khác nhau. Trường được đánh dấu đại diện cho đối tượng khóa được chia sẻ bởi các phương thức này.
Ví dụ: Player.place() và Enemy.place() có thể sử dụng cùng một @Turn_monitor, đảm bảo rằng các phần quan trọng của chúng hoạt động như một chuỗi đơn luồng.
Modifier for_every_thread
Đánh dấu một trường là volatile (hoặc tương đương).
Điều này đảm bảo rằng giá trị mới nhất được nhìn thấy qua các luồng, tránh các vấn đề liên quan đến bộ nhớ đệm. Điều này làm cho tính an toàn đồng thời trở nên rõ ràng ngay lập tức trong mã nguồn.
Các Hoạt Động Phụ Thuộc
Một số hoạt động không thể tiếp tục cho đến khi một hoạt động khác hoàn thành. Clprolf thể hiện điều này với dependent_activity (@Dependent_activity).
Ví dụ: một hộp thư với một khe tin nhắn.
read()phải chờ cho đến khiwrite()đã đặt một tin nhắn.write()phải chờ cho đến khi hộp thư trống.
Cả hai đều được đánh dấu là @Dependent_activity và chia sẻ cùng một @Turn_monitor. Điều này mô phỏng mô hình nhà sản xuất/nhà tiêu thụ mà không cần phải sử dụng thuật ngữ kỹ thuật.
Khuyến Nghị Thiết Kế
- Bắt đầu đơn giản: viết mã đơn luồng trước, sử dụng
long_actionvàprevent_missing_collision. - Thêm tính song song khi cần: áp dụng
one_at_a_time,turn_monitor, vàfor_every_threadchỉ khi thực sự cần đa luồng. - Sử dụng các hoạt động phụ thuộc: chúng làm cho các phụ thuộc giữa các luồng trở nên rõ ràng và giảm độ phức tạp trong suy nghĩ.
Các Cân Nhắc Về Hiệu Suất
- Những modifier này là các hỗ trợ ngữ nghĩa — chúng dựa vào các cấu trúc Java (
synchronized,volatile,wait,notifyAll). - Không có chi phí hiệu suất nào được giới thiệu so với mã Java tương đương.
- Chúng chỉ đơn giản làm cho ý định thiết kế trở nên rõ ràng và nhất quán.
Ví Dụ Mã
Ví Dụ Tác Nhân: Player với long_action và prevent_missing_collision
java
public simu_real_obj Player {
@Long_action
private boolean isFalling = false;
public prevent_missing_collision void place(int xWanted, int yWanted, MovementKind kindWantedMvt) {
if (isSettingUpLadder && checkPlayerInLadder()==null) {
this.isSettingUpLadder = false;
}
if (!checkIfInEnemyPosition(xWanted, yWanted)) {
this.x = xWanted;
this.y = yWanted;
}
}
public long_action void fallIfHole() {
if (!this.isFalling && !isJumping && !isSettingUpLadder && !checkIfNoPlatformBehind()) {
this.isFalling = true;
this.continueFallIfHole();
}
}
public long_action void continueFallIfHole() {
if (this.isFalling && !this.isSettingUpLadder) {
if (!isJumping && !checkIfNoPlatformBehind()) {
this.place(x, y+1, true);
} else {
this.isFalling = false;
}
}
}
public long_action void endLongActions() {
if (this.game.realiz.waitPaintCount(2)) {
continueJump();
}
if (this.game.realiz.waitPaintCount(2)) {
continueFallIfHole();
}
}
}
Ví Dụ Tác Nhân Công Việc: OneMessageMailBox với Các Hoạt Động Phụ Thuộc
java
/* Một lớp với hai hoạt động phụ thuộc */
@Abstraction
public class OneMessageMailBox {
private String message;
@For_every_thread
private boolean full; // Không volatile ở đây, vì được sử dụng trong một khối đồng bộ
@Turn_monitor
private Object mailBoxMonitor; // Đối tượng khóa chia sẻ, cũng được sử dụng cho wait()/notify()
public OneMessageMailBox() {
mailBoxMonitor = new Object();
}
@Dependent_activity
@One_at_a_time
public String read() {
synchronized(mailBoxMonitor) {
while (!this.full) {
try {
this.mailBoxMonitor.wait();
} catch (InterruptedException e) {
Thread.currentThread().interrupt();
}
}
this.full = false;
this.mailBoxMonitor.notifyAll();
return this.message;
}
}
@Dependent_activity
@One_at_a_time
public void write(String message) {
synchronized(mailBoxMonitor) {
while (this.full) {
try {
this.mailBoxMonitor.wait();
} catch (InterruptedException e) {
Thread.currentThread().interrupt();
}
}
this.message = message;
this.full = true;
this.mailBoxMonitor.notifyAll();
}
}
}
Kết Luận
Đồng thời và tính song song vốn phức tạp. Clprolf không cố gắng giấu đi sự phức tạp này, mà thay vào đó cung cấp các công cụ rõ ràng và cụ thể:
long_actionvàprevent_missing_collisioncho đồng thời,one_at_a_time,turn_monitor, vàfor_every_threadcho tính song song,dependent_activityđể làm rõ các phụ thuộc giữa các luồng.
Với những hỗ trợ này, mã nguồn giữ được sự đơn giản, cấu trúc rõ ràng và gần gũi với lý luận thực tế. Clprolf khuyến khích các lập trình viên nghĩ trước tiên theo phương thức đồng thời đơn luồng, và chỉ sau đó mở rộng sang đa luồng khi cần thiết.