Chương : Tiểu trình, tiến trình , đồng Một điểm mạnh hệ điều hành Microsoft Windows cho phép nhiều chương trình (tiến trình—process) chạy đồng thời cho phép tiến trình thực nhiều tác vụ đồng thời (bằng nhiều tiểu trình—thread) Chương trình bày cách kiểm soát tiến trình tiểu trình ứng dụng dựa vào tính thư viện lớp NET Framework cung cấp Các mục chương trình bày cách thực vấn đề sau: Sử dụng kỹ thuật tính khác NET Framework để tạo tiểu trình (mục 4.1 đến 4.5) Kiểm soát trình thực thi tiểu trình để biết kết thúc (mục 4.6 4.7) Đồng hóa trình thực thi nhiều tiểu trình (mục 4.8 4.9) Chạy dừng tiến trình (mục 4.10 4.11) Bảo đảm thời điểm chạy thể ứng dụng (mục 4.12) 1.1 Thực thi phương thức với thread-pool V Bạn cần thực thi phương thức tiểu trình thread-pool # thực thi Khai báo phương thức chứa mã lệnh cần thực thi; phương thức phải trả void nhận đối số Sau đó, tạo thể ủy nhiệm System.Threading.WaitCallback tham chiếu đến phương thức Tiếp tục, gọi phương thức tĩnh QueueUserWorkItem lớp System.Threading.ThreadPool, truyền thể ủy nhiệm tạo làm đối số Bộ thực thi xếp thể ủy nhiệm vào hàng đợi thực thi tiểu trình thread-pool sẵn sàng Nếu ứng dụng sử dụng nhiều tiểu trình có thời gian sống ngắn hay trì số lượng lớn tiểu trình đồng thời hiệu giảm sút chi phí cho việc tạo, vận hành hủy tiểu trình Ngoài ra, hệ thống hỗ-trợ-đa-tiểu-trình, tiểu trình thường trạng thái rỗi suốt khoảng thời gian dài để chờ điều kiện thực thi phù hợp Việc sử dụng thread-pool cung cấp giải pháp chung nhằm cải thiện tính quy mô hiệu hệ thống hỗ-trợ-đa-tiểu-trình .NET Framework cung cấp thực đơn giản cho thread-pool mà truy xuất thông qua thành viên tĩnh lớp ThreadPool Phương thức QueueUserWorkItem cho phép bạn thực thi phương thức tiểu trình thread-pool (đặt công việc vào hàng đợi) Mỗi công việc mô tả thể ủy nhiệm WaitCallback (tham chiếu đến phương thức cần thực thi) Khi tiểu trình thread-pool sẵn sàng, nhận công việc từ hàng đợi thực thi công việc Khi hoàn tất công việc, thay kết thúc, tiểu trình quay thread-pool nhận công việc từ hàng đợi Việc sử dụng thread-pool thực thi giúp đơn giản hóa việc lập trình hỗ-trợ-đa-tiểutrình Tuy nhiên, cần lưu ý thread-pool thực đơn giản, nhằm mục đích sử dụng chung Trước định sử dụng thread-pool này, cần xem xét điểm sau: • Bộ thực thi quy định số tiểu trình tối đa cấp cho thread-pool; bạn thay đổi số tối đa tham số cấu hình hay từ bên mã được-quản-lý Giới hạn mặc định 25 tiểu trình cho CPU hệ thống Số tiểu trình tối đa thread-pool không giới hạn số công việc chờ hàng đợi • Cũng việc cho phép bạn sử dụng thread-pool để thực thi mã lệnh cách trực tiếp, thực thi sử dụng thread-pool cho nhiều mục đích bên trong, bao gồm việc thực thi phương thức cách bất đồng (xem mục 4.2) thực thi kiện định thời (xem mục 4.3) Tất công việc dẫn đến tranh chấp tiểu trình thread-pool; nghĩa hàng đợi trở nên dài Mặc dù độ dài tối đa hàng đợi bị giới hạn số lượng nhớ lại cho tiến trình thực thi, hàng đợi dài làm kéo dài trình thực thi công việc hàng đợi • Bạn không nên sử dụng thread-pool để thực thi tiến trình chạy thời gian dài Vì số tiểu trình thread-pool có giới hạn, nên số tiểu trình thuộc tiến trình loại ảnh hưởng đáng kể đến toàn hiệu thread-pool Đặc biệt, bạn nên tránh đặt tiểu trình thread-pool vào trạng thái đợi thời gian dài • Bạn điều khiển lịch trình tiểu trình thread-pool, thay đổi độ ưu tiên công việc Thread-pool xử lý công việc theo thứ tự bạn thêm chúng vào hàng đợi • Một công việc đặt vào hàng đợi bạn hủy hay dừng Ví dụ trình bày cách sử dụng lớp ThreadPool để thực thi phương thức có tên DisplayMessage Ví dụ truyền DisplayMessage đến thread-pool hai lần, lần đầu đối số, lần sau có đối số đối tượng MessageInfo (cho phép kiểm soát thông tin mà tiểu trình hiển thị) using System; using System.Threading; // Lớp dùng để truyền liệu cho phương thức DisplayMessage // thực thi thread-pool public class MessageInfo { private int iterations; private string message; // Phương thức khởi dựng nhận thiết lập cấu hình cho tiểu trình public MessageInfo(int iterations, string message) { this.iterations = iterations; this.message = message; } // Các thuộc tính dùng để lấy thiết lập cấu hình public int Iterations { get { return iterations; } } public string Message { get { return message; } } } public class ThreadPoolExample { // Hiển thị thông tin cửa sổ Console public static void DisplayMessage(object state) { // Ép đối số state sang MessageInfo MessageInfo config = state as MessageInfo; // Nếu đối số config null, đối số // truyền cho phương thức ThreadPool.QueueUserWorkItem; // sử dụng giá trị mặc định if (config == null) { // Hiển thị thông báo cửa sổ Console ba lần for (int count = 0; count < 3; count++) { Console.WriteLine("A thread-pool example."); // Vào trạng thái chờ, dùng cho mục đích minh họa // Tránh đưa tiểu trình thread-pool // vào trạng thái chờ ứng dụng thực tế Thread.Sleep(1000); } } else { // Hiển thị thông báo định trước // với số lần định trước for (int count = 0; count < config.Iterations; count++) { Console.WriteLine(config.Message); // Vào trạng thái chờ, dùng cho mục đích minh họa // Tránh đưa tiểu trình thread-pool // vào trạng thái chờ ứng dụng thực tế Thread.Sleep(1000); } } } public static void Main() { // Tạo đối tượng ủy nhiệm, cho phép // truyền phương thức DisplayMessage cho thread-pool WaitCallback workMethod = new WaitCallback(ThreadPoolExample.DisplayMessage); // Thực thi DisplayMessage thread-pool (không có đối số) ThreadPool.QueueUserWorkItem(workMethod); // Thực thi DisplayMessage thread-pool (truyền // đối tượng MessageInfo cho phương thức DisplayMessage) MessageInfo info = new MessageInfo(5, "A thread-pool example with arguments."); ThreadPool.QueueUserWorkItem(workMethod, info); // Nhấn Enter để kết thúc Console.WriteLine("Main method complete Press Enter."); Console.ReadLine(); } } 1.2 Thực thi phương thức cách bất đồng V Bạn cần thực thi phương thức tiếp tục thực công việc khác # phương thức chạy tiểu trình riêng biệt Sau phương thức hoàn tất, bạn cần lấy trị trả Khai báo ủy nhiệm có chữ ký giống phương thức cần thực thi Sau đó, tạo thể ủy nhiệm tham chiếu đến phương thức Tiếp theo, gọi phương thức BeginInvoke thể ủy nhiệm để thực thi phương thức bạn Kế đến, sử dụng phương thức EndInvoke để kiểm tra trạng thái phương thức thu lấy trị trả hoàn tất Khi cho gọi phương thức, thường thực cách đồng bộ; nghĩa mã lệnh thực lời gọi phải vào trạng thái dừng (block) phương thức thực xong Đây cách cần thiết mã lệnh yêu cầu trình thực thi phương thức phải hoàn tất trước tiếp tục Tuy nhiên, số trường hợp, bạn lại cần thực thi phương thức cách bất đồng bộ; nghĩa bạn cho thực thi phương thức tiểu trình riêng tiếp tục thực công việc khác .NET Framework hỗ trợ chế độ thực thi bất đồng bộ, cho phép bạn thực thi phương thức cách bất đồng ủy nhiệm Khi khai báo biên dịch ủy nhiệm, trình biên dịch tự động sinh hai phương thức hỗ trợ chế độ thực thi bất đồng bộ: BeginInvoke EndInvoke Khi bạn gọi phương thức BeginInvoke thể ủy nhiệm, phương thức tham chiếu ủy nhiệm xếp vào hàng đợi để thực thi bất đồng Quyền kiểm soát trình thực thi trả cho mã gọi BeginInvoke sau đó, phương thức tham chiếu thực thi ngữ cảnh tiểu trình sẵn sàng trước tiên thread-pool Các đối số phương thức BeginInvoke gồm đối số định ủy nhiệm, cộng với hai đối số dùng phương thức thực thi bất đồng kết thúc Hai đối số là: • Một thể ủy nhiệm System.AsyncCallback tham chiếu đến phương thức mà thực thi gọi phương thức thực thi bất đồng kết thúc Phương thức thực thi ngữ cảnh tiểu trình thread-pool Truyền giá trị null cho đối số nghĩa phương thức gọi bạn phải sử dụng chế khác để xác định phương thức thực thi bất kết thúc (sẽ thảo luận bên dưới) • Một tham chiếu đối tượng mà thực thi liên kết với trình thực thi bất đồng Phương thức thực thi bất đồng sử dụng hay truy xuất đến đối tượng này, mã lệnh bạn sử dụng phương thức kết thúc, cho phép bạn liên kết thông tin trạng thái với trình thực thi bất đồng Ví dụ, đối tượng cho phép bạn ánh xạ kết với thao tác bất đồng khởi tạo trường hợp bạn khởi tạo nhiều thao tác bất đồng sử dụng chung phương thức callback để xử lý việc kết thúc Phương thức EndInvoke cho phép bạn lấy trị trả phương thức thực thi bất đồng bộ, trước hết bạn phải xác định kết thúc Dưới bốn kỹ thuật dùng để xác định phương thức thực thi bất đồng kết thúc hay chưa: • Blocking—dừng trình thực thi tiểu trình hành phương thức thực thi bất đồng kết thúc Điều giống với thực thi đồng Tuy nhiên, linh hoạt chọn thời điểm xác để đưa mã lệnh bạn vào trạng thái dừng (block) bạn hội thực thêm số việc trước mã lệnh vào trạng thái • Polling—lặp lặp lại việc kiểm tra trạng thái phương thức thực thi bất đồng để xác định kết thúc hay chưa Đây kỹ thuật đơn giản, xét mặt xử lý không hiệu Bạn nên tránh vòng lặp chặt làm lãng phí thời gian xử lý; tốt nên đặt tiểu trình thực polling vào trạng thái nghỉ (sleep) khoảng thời gian cách sử dụng Thread.Sleep lần kiểm tra trạng thái Bởi kỹ thuật polling đòi hỏi bạn phải trì vòng lặp nên hoạt động tiểu trình chờ bị giới hạn, nhiên bạn dễ dàng cập nhật tiến độ công việc • Waiting—sử dụng đối tượng dẫn xuất từ lớp System.Threading.WaitHandle để báo hiệu phương thức thực thi bất đồng kết thúc Waiting cải tiến kỹ thuật polling, cho phép bạn chờ nhiều phương thức thực thi bất đồng kết thúc Bạn định giá trị time-out cho phép tiểu trình thực waiting dừng lại phương thức thực thi bất đồng diễn lâu, bạn muốn cập nhật định kỳ trạng thái • Callbacks—Callback phương thức mà thực thi gọi phương thức thực thi bất đồng kết thúc Mã lệnh thực lời gọi không cần thực thao tác kiểm tra nào, tiếp tục thực công việc khác Callback linh hoạt phức tạp, đặc biệt có nhiều phương thức thực thi bất đồng chạy đồng thời sử dụng callback Trong trường hợp thế, bạn phải sử dụng đối tượng trạng thái thích hợp để so trùng phương thức hoàn tất với phương thức khởi tạo Lớp AsyncExecutionExample ví dụ mô tả chế thực thi bất đồng Nó sử dụng ủy nhiệm có tên AsyncExampleDelegate để thực thi bất đồng phương thức có tên LongRunningMethod Phương thức LongRunningMethod sử dụng Thread.Sleep để mô phương thức có thời gian thực thi dài // Ủy nhiệm cho phép bạn thực việc thực thi bất đồng // AsyncExecutionExample.LongRunningMethod public delegate DateTime AsyncExampleDelegate(int delay, string name); // Phương thức có thời gian thực thi dài public static DateTime LongRunningMethod(int delay, string name) { Console.WriteLine("{0} : {1} example - thread starting.", DateTime.Now.ToString("HH:mm:ss.ffff"), name); // Mô việc xử lý tốn nhiều thời gian Thread.Sleep(delay); Console.WriteLine("{0} : {1} example - thread finishing.", DateTime.Now.ToString("HH:mm:ss.ffff"), name); // Trả thời gian hoàn tất phương thức return DateTime.Now; } AsyncExecutionExample chứa năm phương thức diễn đạt cách tiếp cận khác việc kết thúc phương thức thực thi bất đồng Dưới mô tả cung cấp mã lệnh cho phương thức Phương thức BlockingExample Phương thức BlockingExample thực thi bất đồng phương thức LongRunningMethod tiếp tục thực công việc khoảng thời gian Khi xử lý xong công việc này, BlockingExample chuyển sang trang thái dừng (block) phương thức LongRunningMethod kết thúc Để vào trạng thái dừng, BlockingExample thực thi phương thức EndInvoke thể ủy nhiệm AnsyncExampleDelegate Nếu phương thức LongRunningMethod kết thúc, EndInvoke trả lập tức, không, BlockingExample chuyển sang trạng thái dừng phương thức LongRunningMethod kết thúc ... trình thread-pool có giới hạn, nên số tiểu trình thuộc tiến trình loại ảnh hưởng đáng kể đến toàn hiệu thread-pool Đặc biệt, bạn nên tránh đặt tiểu trình thread-pool vào trạng thái đợi thời gian... nhớ lại cho tiến trình thực thi, hàng đợi dài làm kéo dài trình thực thi công việc hàng đợi • Bạn không nên sử dụng thread-pool để thực thi tiến trình chạy thời gian dài Vì số tiểu trình thread-pool... tất công việc, thay kết thúc, tiểu trình quay thread-pool nhận công việc từ hàng đợi Việc sử dụng thread-pool thực thi giúp đơn giản hóa việc lập trình hỗ-trợ-đa-tiểutrình Tuy nhiên, cần lưu ý