Đồng bộ hóa luồng
Ứng dụng đa luồng (Multi-Threading)
Với C #, bạn có thể viết các ứng dụng thực hiện nhiều tác vụ cùng một lúc. Các tác vụ có tiềm năng giữ các nhiệm vụ khác có thể thực hiện trên các luồng riêng biệt, một quá trình được gọi là luồng luồng nhiều luồng hoặc luồng luồng miễn phí.
Các ứng dụng sử dụng đa luồng phản hồi nhanh hơn với đầu vào của người dùng vì giao diện người dùng vẫn hoạt động khi các tác vụ xử lý thực hiện trên các luồng riêng biệt. Đa luồng cũng hữu ích khi bạn tạo các ứng dụng có thể mở rộng, bởi vì bạn có thể thêm chủ đề khi khối lượng công việc tăng lên.
Một trong những lợi ích của việc sử dụng nhiều luồng trong một ứng dụng là mỗi luồng thực thi không đồng bộ. Đối với các ứng dụng Windows, điều này cho phép các tác vụ tốn thời gian được thực hiện trong nền trong khi cửa sổ ứng dụng và các điều khiển vẫn đáp ứng. Đối với các ứng dụng máy chủ, đa luồng cung cấp khả năng xử lý mỗi yêu cầu gửi đến với một chủ đề khác. Nếu không, mỗi yêu cầu mới sẽ không được phục vụ cho đến khi yêu cầu trước đó đã được thỏa mãn đầy đủ.
Tuy nhiên, tính chất không đồng bộ của các luồng có nghĩa là truy cập vào các tài nguyên như các tập tin xử lý, kết nối mạng, và bộ nhớ phải được phối hợp. Nếu không, hai hoặc nhiều chủ đề có thể truy cập cùng một tài nguyên cùng một lúc, mỗi người đều không biết đến hành động của người khác. Kết quả là tham nhũng dữ liệu không thể đoán trước.
Minh hoạ sau sẽ giúp bạn hiểu hơn về các cách thức để thực hiện Thread Synchronization:
Từ khoá lock
Câu lệnh C # lock có thể được sử dụng để đảm bảo rằng một khối mã chạy để hoàn thành mà không bị gián đoạn bởi các chủ đề khác. Điều này được thực hiện bằng cách lấy một khóa loại trừ lẫn nhau cho một đối tượng đã cho trong khoảng thời gian của khối mã.
Một tuyên bố khóa được cho một đối tượng như là một đối số, và được theo sau bởi một khối mã mà sẽ được thực hiện bởi chỉ một luồng một lúc. Ví dụ:
public class TestThreading { private System.Object lockThis = new System.Object(); public void Process() { lock (lockThis) { // Access thread-sensitive resources. } } }
Đối số được cung cấp cho khóa khóa phải là một đối tượng dựa trên một loại tham chiếu, và được sử dụng để xác định phạm vi của khóa. Trong ví dụ trên, phạm vi khóa được giới hạn trong chức năng này vì không có tham chiếu đến khóa đối tượng. Điều này tồn tại bên ngoài hàm. Nếu như một tham chiếu đã tồn tại, khóa phạm vi sẽ mở rộng đến đối tượng đó. Nói đúng ra, đối tượng được cung cấp chỉ được sử dụng để xác định duy nhất tài nguyên đang được chia sẻ giữa nhiều luồng, vì vậy nó có thể là một thể hiện lớp tùy ý. Tuy nhiên, trong thực tế, đối tượng này thường đại diện cho tài nguyên cần đồng bộ hóa luồng. Ví dụ, nếu đối tượng thùng chứa được sử dụng bởi nhiều luồng, thì thùng chứa có thể được chuyển để khóa và khối mã đồng bộ theo khóa sẽ truy cập vào vùng chứa. Miễn là các chủ đề khác khóa trên cùng một chứa trước khi truy cập vào nó, sau đó truy cập vào đối tượng được đồng bộ hóa an toàn.
Monitor
Giống như khóa khóa, màn hình ngăn chặn các khối mã từ việc thực hiện đồng thời bởi nhiều luồng. Phương thức Enter cho phép một và chỉ một thread để tiến hành các câu sau đây; tất cả các chủ đề khác bị chặn cho đến khi thread thực thi gọi Exit. Điều này cũng giống như sử dụng khóa khóa. Cú pháp:
lock (x) { DoSomething(); }
Ví dụ cụ thể:
System.Object obj = (System.Object)x; System.Threading.Monitor.Enter(obj); try { DoSomething(); } finally { System.Threading.Monitor.Exit(obj); }
Sự kiện đồng bộ hoá và quản lý Wait
Việc sử dụng khóa hoặc màn hình rất hữu ích để ngăn chặn việc thực hiện đồng thời các khối mã mã vạch, nhưng các cấu trúc này không cho phép một chủ đề truyền thông sự kiện tới một luồng khác. Điều này đòi hỏi sự kiện đồng bộ hóa, là các đối tượng có một trong hai trạng thái, báo hiệu và không báo hiệu, có thể được sử dụng để kích hoạt và treo các chủ đề. Các chủ đề có thể bị đình chỉ bằng cách chờ đợi cho một sự kiện đồng bộ hoá không được công nhận, và có thể được kích hoạt bằng cách thay đổi trạng thái sự kiện để báo hiệu. Nếu một thread cố gắng để chờ đợi một sự kiện đã được báo hiệu, sau đó thread tiếp tục thực hiện mà không có sự chậm trễ.
using System; using System.Threading; class ThreadingExample { static AutoResetEvent autoEvent; static void DoWork() { Console.WriteLine("Worker thread started, now waiting on event..."); autoEvent.WaitOne(); Console.WriteLine("Worker thread reactivated, now exiting..."); } static void Main() { autoEvent = new AutoResetEvent(false); Console.WriteLine("main thread starting worker thread..."); Thread t = new Thread(DoWork); t.Start(); Console.WriteLine("main thread sleeping for 1 second..."); Thread.Sleep(1000); Console.WriteLine("main thread signaling worker thread..."); autoEvent.Set(); } }
Khoá chết (Deadlock)
Đồng bộ hóa các chủ đề là vô giá trong các ứng dụng đa luồng, nhưng luôn luôn có nguy cơ tạo ra một bế tắc, trong đó nhiều luồng đang chờ đợi cho nhau và ứng dụng bị dừng lại. Bế tắc là tương tự như tình huống trong đó xe dừng lại tại một điểm dừng bốn chiều và mỗi người đang đợi người kia đi. Tránh deadlocks là quan trọng; chính là lập kế hoạch cẩn thận. Bạn thường có thể dự đoán tình huống bế tắc bằng cách sơ đồ các ứng dụng đa luồng trước khi bạn bắt đầu viết mã.