CÁC GIẢI PHÁP LẬP TRÌNH C# - Trang 144

144
Chương 4: Tiểu trình, tiến trình, và sự đồng bộ

Thách thức lớn nhất trong việc viết một ứng dụng hỗ-trợ-đa-tiểu-trình là bảo đảm các tiểu
trình làm việc trong sự hòa hợp. Việc này thường được gọi là “đồng bộ hóa tiểu trình” và bao
gồm:

Bảo đảm các tiểu trình truy xuất các đối tượng và dữ liệu dùng chung một cách phù hợp
để không gây ra sai lạc.

Bảo đảm các tiểu trình chỉ thực thi khi thật sự cần thiết và phải đảm bảo rằng chúng chỉ
được thực thi với chi phí tối thiểu khi chúng rỗi.

Cơ chế đồng bộ hóa thông dụng nhất là lớp

Monitor

. Lớp này cho phép một tiểu trình đơn thu

lấy chốt (lock) trên một đối tượng bằng cách gọi phương thức tĩnh

Monitor.Enter

. Bằng cách

thu lấy chốt trước khi truy xuất một tài nguyên hay dữ liệu dùng chung, ta chắc chắn rằng chỉ
có một tiểu trình có thể truy xuất tài nguyên đó cùng lúc. Một khi đã hoàn tất với tài nguyên,
tiểu trình này sẽ giải phóng chốt để tiểu trình khác có thể truy xuất nó. Khối mã thực hiện
công việc này thường được gọi là vùng hành căng (critical section).
Bạn có thể sử dụng bất kỳ đối tượng nào đóng vai trò làm chốt, và sử dụng từ khóa

this

để

thu lấy chốt trên đối tượng hiện tại. Điểm chính là tất cả các tiểu trình khi truy xuất một tài
nguyên dùng chung phải thu lấy cùng một chốt. Các tiểu trình khác khi thu lấy chốt trên cùng
một đối tượng sẽ block (đi vào trạng thái

WaitSleepJoin

) và được thêm vào hàng sẵn sàng

(ready queue) của chốt này cho đến khi tiểu trình chủ giải phóng nó bằng phương thức tĩnh

Monitor.Exit

. Khi tiểu trình chủ gọi

Exit

, một trong các tiểu trình từ hàng sẵn sàng sẽ thu lấy

chốt. Nếu tiểu trình chủ không giải phóng chốt bằng

Exit

, tất cả các tiểu trình khác sẽ block

vô hạn định. Vì vậy, cần đặt lời gọi

Exit

bên trong khối

finally

để bảo đảm nó được gọi cả

khi ngoại lệ xảy ra.

Monitor

thường xuyên được sử dụng trong các ứng dụng hỗ-trợ-đa-tiểu-trình nên C# cung

cấp hỗ trợ mức-ngôn-ngữ thông qua lệnh

lock

. Khối mã được gói trong lệnh

lock

tương

đương với gọi

Monitor.Enter

khi đi vào khối mã này, và gọi

Monitor.Exit

khi đi ra khối mã

này. Ngoài ra, trình biên dịch tự động đặt lời gọi

Monitor.Exit

trong khối

finally

để bảo đảm

chốt được giải phóng khi một ngoại lệ bị ném.
Tiểu trình chủ (sở hữu chốt) có thể gọi

Monitor.Wait

để giải phóng chốt và đặt tiểu trình này

vào hàng chờ (wait queue). Các tiểu trình trong hàng chờ cũng có trạng thái là

WaitSleepJoin

và sẽ tiếp tục block cho đến khi tiểu trình chủ gọi phương thức

Pulse

hay

PulseAll

của lớp

Monitor

. Phương thức

Pulse

di chuyển một trong các tiểu trình từ hàng chờ vào hàng sẵn

sàng, còn phương thức

PulseAll

thì di chuyển tất cả các tiểu trình. Khi một tiểu trình đã được

di chuyển từ hàng chờ vào hàng sẵn sàng, nó có thể thu lấy chốt trong lần giải phóng kế tiếp.
Cần hiểu rằng các tiểu trình thuộc hàng chờ sẽ không thu được chốt, chúng sẽ đợi vô hạn định
cho đến khi bạn gọi

Pulse

hay

PulseAll

để di chuyển chúng vào hàng sẵn sàng. Sử dụng

Wait

Pulse

là cách phổ biến khi thread-pool được sử dụng để xử lý các item từ một hàng đợi

dùng chung.
Lớp

ThreadSyncExample

dưới đây trình bày cách sử dụng lớp

Monitor

và lệnh

lock

. Ví dụ này

khởi chạy ba tiểu trình, mỗi tiểu trình (lần lượt) thu lấy chốt của một đối tượng có tên là

consoleGate

. Kế đó, mỗi tiểu trình gọi phương thức

Monitor.Wait

. Khi người dùng nhấn

Enter lần đầu tiên,

Monitor.Pulse

sẽ được gọi để giải phóng một tiểu trình đang chờ. Lần thứ

Liên Kết Chia Sẽ

** Đây là liên kết chia sẻ bới cộng đồng người dùng, chúng tôi không chịu trách nhiệm gì về nội dung của các thông tin này. Nếu có liên kết nào không phù hợp xin hãy báo cho admin.