Bạn cần thực thi một phương thức và tiếp tục thực hiện các công việc khác trong khi phương thức này vẫn chạy trong một tiểu trình riêng biệt. Sau khi phương thức đã hoàn tất, bạn cần lấy trị trả về của nó. Hãy thực hiện khai báo một ủy nhiệm có chữ ký giống như phương thức cần thực thi. Sau đó, tạo một thể hiện của ủy nhiệm tham chiếu đến phương thức này. Tiếp theo, gọi phương thức BeginInvoke của thể hiện ủy nhiệm để thực thi phương thức của bạn. Kế đến, sử dụng phương thức EndInvoke để kiểm tra trạng thái của phương thức cũng như thu lấy trị trả về của nó nếu đã hoàn tất.
Khi cho gọi một phương thức, chúng ta thường thực hiện một cách đồng bộ; nghĩa là mã lệnh thực hiện lời gọi phải đi vào trạng thái dừng (block) cho đến khi phương thức được thực hiện xong. Đây là cách cần thiết khi mã lệnh yêu cầu quá trình thực thi phương thức phải hoàn tất trước khi nó có thể tiếp tục. Tuy nhiên, trong một số trường hợp, bạn lại cần thực thi phương thức một cách bất đồng bộ; nghĩa là bạn cho thực thi phương thức này trong một tiểu trình riêng trong khi vẫn tiếp tục thực hiện cá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 bất kỳ phương thức nào một cách bất đồng bộ bằng một ủy nhiệm. Khi khai báo và biên dịch một ủy nhiệm, trình biên dịch sẽ tự động sinh ra hai phương thức hỗ trợ chế độ thực thi bất đồng bộ: BeginInvoke và EndInvoke. Khi bạn gọi phương thức BeginInvoke của một thể hiện ủy nhiệm, phương thức được tham chiếu bởi ủy nhiệm này được xếp vào hàng đợi để thực thi bất đồng bộ. Quyền kiểm soát quá trình thực thi được trả về cho mã gọi BeginInvoke ngay sau đó, và phương thức được tham chiếu sẽ thực thi trong ngữ cảnh của tiểu trình sẵn sàng trước tiên trong Thread-pool.
Các đối số của phương thức BeginInvoke gồm các đối số được chỉ định bởi ủy nhiệm, cộng với hai đối số dùng khi phương thức thực thi bất đồng bộ kết thúc. Hai đối số này là:
• Một thể hiện của ủy nhiệm System.AsyncCallback tham chiếu đến phương thức mà bộ thực thi sẽ gọi khi phương thức thực thi bất đồng bộ kết thúc. Phương thức này sẽ được thực thi trong ngữ cảnh của một tiểu trình trong Thread-pool. Truyền giá trị null cho đối số này nghĩa là không có phương thức nào được gọi và bạn phải sử dụng một cơ chế khác để xác định khi nào phương thức thực thi bất bộ kết thúc (sẽ được thảo luận bên dưới).
• Một tham chiếu đối tượng mà bộ thực thi sẽ liên kết với quá trình thực thi bất đồng bộ. Phương thức thực thi bất đồng bộ không thể sử dụng hay truy xuất đến đối tượng này, nhưng mã lệnh của bạn có thể sử dụng nó khi phương thức này kết thúc, cho phép bạn liên kết thông tin trạng thái với quá trình thực thi bất đồng bộ. Ví dụ, đối tượng này cho phép bạn ánh xạ các kết quả với các thao tác bất đồng bộ đã được khởi tạo trong trường hợp bạn khởi tạo nhiều thao tác bất đồng bộ nhưng sử dụng chung một 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ả về của phương thức thực thi bất đồng bộ, nhưng trước hết bạn phải xác định khi nào nó kết thúc. Dưới đây là bốn kỹ thuật dùng để xác định một phương thức thực thi bất đồng bộ đã kết thúc hay chưa:
• Blocking—dừng quá trình thực thi của tiểu trình hiện hành cho đến khi phương thức thực thi bất đồng bộ kết thúc. Điều này rất giống với sự thực thi đồng bộ. Tuy nhiên, nếu linh hoạt chọn thời điểm chính xác để đưa mã lệnh của bạn vào trạng thái dừng (block) thì bạn vẫn còn cơ hội thực hiện thêm một số việc trước khi mã lệnh đi vào trạng thái này.
• Polling—lặp đi lặp lại việc kiểm tra trạng thái của phương thức thực thi bất đồng bộ để xác định nó kết thúc hay chưa. Đây là một kỹ thuật rất đơn giản, nhưng nếu xét về mặt xử lý thi không được hiệu quả. Bạn nên tránh các vòng lặp chặt làm lãng phí thời gian của bộ xử lý; tốt nhất là nên đặt tiểu trình thực hiện polling vào trạng thái nghỉ (sleep) trong một khoảng thời gian bằng cách sử dụng Thread.Sleep giữa các lần kiểm tra trạng thái. Bởi kỹ thuât polling đòi hỏi bạn phải duy trì môt vòng lặp nên hoạt đông của tiểu trình chờ sẽ bị giới hạn, tuy nhiên bạn có thể dễ dàng cập nhật tiến độ công viêc.
• Waiting—sử dụng một đối tượng dẫn xuất từ lớp System.Threading.WaitHandle để báo hiệu khi phương thức thực thi bất đồng bộ kết thúc. Waiting là một cải tiến của kỹ thuật polling, nó cho phép bạn chờ nhiều phương thức thực thi bất đồng bộ kết thúc. Bạn cũng có thể chỉ định các giá trị time-out cho phép tiểu trình thực hiện waiting dừng lại nếu phương thức thực thi bất đồng bộ đã diễn ra quá lâu, hoặc bạn muốn cập nhật định kỳ bộ chỉ trạng thái.
• Callbacks—Callback là một phương thức mà bộ thực thi sẽ gọi khi phương thức thực thi bất đồng bộ kết thúc. Mã lệnh thực hiện lời gọi không cần thực hiện bất kỳ thao tác kiểm tra nào, nhưng vẫn có thể tiếp tục thực hiện các công việc khác. Callback rất linh hoạt nhưng cũng rất phức tạp, đặc biệt khi có nhiều phương thức thực thi bất đồng bộ chạy đồng thời nhưng sử dụng cùng một callback. Trong những trường hợp như thế, bạn phải sử dụng các đối tượng trạng thái thích hợp để so trùng các phương thức đã hoàn tất với các phương thức đã khởi tạo.
Lớp AsyncExecutionExample trong ví dụ dưới đây mô tả cơ chế thực thi bất đồng bộ. Nó sử dụng một ủy nhiệm có tên là AsyncExampleDelegate để thực thi bất đồng bộ một phương thức có tên là LongRunningMethod. Phương thức LongRunningMethod sử dụng Thread.Sleep để mô phỏng một phương thức có thời gian thực thi dài.
-
-
- public delegate DateTime AsyncExampleDelegate(int delay, string name);
-
- public static DateTime LongRunningMethod(int delay, string name) {
- Console.WriteLine("{0} : {1} example - thread starting.", DateTime.Now.ToString("HH:mm:ss.ffff"), name);
-
- Thread.Sleep(delay);
- Console.WriteLine("{0} : {1} example - thread finishing.", DateTime.Now.ToString("HH:mm:ss.ffff"), name);
-
- return DateTime.Now;
- }
AsyncExecutionExample chứa năm phương thức diễn đạt các cách tiếp cận khác nhau về việc kết thúc phương thức thực thi bất đồng bộ. Dưới đây sẽ mô tả và cung cấp mã lệnh cho các phương thức đó.
1. Phương thức BlockingExample
Phương thức BlockingExample thực thi bất đồng bộ phương thức LongRunningMethod và tiếp tục thực hiện công việc của nó trong một khoảng thời gian. Khi xử lý xong công việc này, BlockingExample chuyển sang trạng thái dừng (block) cho đến khi 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 của thể hiện ủy nhiệm AnsyncExampleDelegate. Nếu phương thức LongRunningMethod kết thúc, EndInvoke trả về ngay lập tức, nếu không, BlockingExample chuyển sang trạng thái dừng cho đến khi phương thức LongRunningMethod kết thúc.
- public static void BlockingExample() {
- Console.WriteLine(Environment.NewLine + "*** Running Blocking Example ***");
-
-
- AsyncExampleDelegate longRunningMethod =new AsyncExampleDelegate(LongRunningMethod);
- IAsyncResult asyncResult = longRunningMethod.BeginInvoke(2000, "Blocking", null, null);
-
-
- for (int count = 0; count < 3; count++) {
- Console.WriteLine("{0} : Continue processing until " +"ready to block...",DateTime.Now.ToString("HH:mm:ss.ffff"));
- Thread.Sleep(200);
- }
-
-
- Console.WriteLine("{0} : Blocking until method is " +"complete...", DateTime.Now.ToString("HH:mm:ss.ffff"));
- DateTime completion =longRunningMethod.EndInvoke(asyncResult);
-
- Console.WriteLine("{0} : Blocking example complete.", completion.ToString("HH:mm:ss.ffff"));
- }
2. Phương thức PollingExample
Phương thức PollingExample thực thi bất đồng bộ phương thức LongRunningMethod và sau đó thực hiện vòng lặp polling cho đến khi LongRunningMethod kết thúc. PollingExample kiểm tra thuộc tính IsComplete của thể hiện IAsyncResult (được trả về bởi BeginInvoke) để xác định phương thức LongRunningMethod đã kết thúc hay chưa, nếu chưa, PollingExample sẽ gọi Thread.Sleep.
- public static void PollingExample() {
- Console.WriteLine(Environment.NewLine + "*** Running Polling Example ***");
-
-
- AsyncExampleDelegate longRunningMethod =new AsyncExampleDelegate(LongRunningMethod);
- IAsyncResult asyncResult = longRunningMethod.BeginInvoke(2000, "Polling", null, null);
-
-
-
-
- Console.WriteLine("{0} : Poll repeatedly until method is " + "complete...", DateTime.Now.ToString("HH:mm:ss.ffff"));
- while(!asyncResult.IsCompleted) {
- Console.WriteLine("{0} : Polling...", DateTime.Now.ToString("HH:mm:ss.ffff"));
- Thread.Sleep(300);
- }
-
- DateTime completion =longRunningMethod.EndInvoke(asyncResult);
-
- Console.WriteLine("{0} : Polling example complete.", completion.ToString("HH:mm:ss.ffff"));
- }
3. Phương thức WaitingExample
Phương thức WaitingExample thực thi bất đồng bộ phương thức LongRunningExample và sau đó chờ cho đến khi LongRunningMethod kết thúc. WaitingExample sử dụng thuộc tính AsyncWaitHandle của thể hiện IAsyncResult (được trả về bởi BeginInvoke) để có được một WaitHandle và sau đó gọi phương thức WaitOne của WaitHandle. Việc sử dụng giá trị time-out cho phép WaitingExample dừng quá trình đợi để thực hiện công việc khác hoặc dừng hoàn toàn nếu phương thức thực thi bất đồng bộ diễn ra quá lâu.
- public static void WaitingExample() {
- Console.WriteLine(Environment.NewLine + "*** Running Waiting Example ***");
-
-
- AsyncExampleDelegate longRunningMethod =new AsyncExampleDelegate(LongRunningMethod);
- IAsyncResult asyncResult = longRunningMethod.BeginInvoke(2000, "Waiting", null, null);
-
-
-
- Console.WriteLine("{0} : Waiting until method is complete...", DateTime.Now.ToString("HH:mm:ss.ffff"));
- while(!asyncResult.AsyncWaitHandle.WaitOne(300, false)) {
- Console.WriteLine("{0} : Wait timeout...",DateTime.Now.ToString("HH:mm:ss.ffff"));
- }
-
- DateTime completion =longRunningMethod.EndInvoke(asyncResult);
-
- Console.WriteLine("{0} : Waiting example complete.", completion.ToString("HH:mm:ss.ffff"));
- }
4. Phương thức WaitAllExample
Phương thức WaitAllExample thực thi bất đồng bộ phương thức LongRunningMethod nhiều lần và sau đó sử dụng mảng các đối tượng WaitHandle để đợi cho đến khi tất cả các phương thức kết thúc.
- public static void WaitAllExample() {
- Console.WriteLine(Environment.NewLine + "*** Running WaitAll Example ***");
-
-
- ArrayList asyncResults = new ArrayList(3);
-
-
-
-
- AsyncExampleDelegate longRunningMethod =new AsyncExampleDelegate(LongRunningMethod);
- asyncResults.Add(longRunningMethod.BeginInvoke(3000, "WaitAll 1", null, null));
- asyncResults.Add(longRunningMethod.BeginInvoke(2500, "WaitAll 2", null, null));
- asyncResults.Add(longRunningMethod.BeginInvoke(1500, "WaitAll 3", null, null));
-
-
-
- WaitHandle[] waitHandles = new WaitHandle[3];
- for (int count = 0; count < 3; count++) {
- waitHandles[count] =
- ((IAsyncResult)asyncResults[count]).AsyncWaitHandle;
- }
-
-
-
- Console.WriteLine("{0} : Waiting until all 3 methods are " +"complete...", DateTime.Now.ToString("HH:mm:ss.ffff"));
- while(!WaitHandle.WaitAll(waitHandles, 300, false)) {
- Console.WriteLine("{0} : WaitAll timeout...",DateTime.Now.ToString("HH:mm:ss.ffff"));
- }
-
-
- DateTime completion = DateTime.MinValue;
- foreach (IAsyncResult result in asyncResults) {
- DateTime time = longRunningMethod.EndInvoke(result);
- if ( time > completion) completion = time;
- }
-
- Console.WriteLine("{0} : WaitAll example complete.", completion.ToString("HH:mm:ss.ffff"));
- }
5. Phương thức CallbackExample
Phương thức CallbackExample thực thi bất đồng bộ phương thức LongRunningMethod và truyền một thể hiện ủy nhiệm AsyncCallback (tham chiếu đến phương thức CallbackHandler) cho phương thức BeginInvoke. Phương thức CallbackHandler sẽ được gọi một cách tự động khi phương thức LongRunningMethod kết thúc, kết quả là phương thức CallbackExample vẫn tiếp tục thực hiện công việc.
- public static void CallbackExample() {
- Console.WriteLine(Environment.NewLine + "*** Running Callback Example ***");
-
-
-
-
-
-
-
-
- AsyncExampleDelegate longRunningMethod =new AsyncExampleDelegate(LongRunningMethod);
- IAsyncResult asyncResult = longRunningMethod.BeginInvoke(2000, "Callback", new AsyncCallback(CallbackHandler), longRunningMethod);
-
- for (int count = 0; count < 15; count++) {
- Console.WriteLine("{0} : Continue processing...",
- DateTime.Now.ToString("HH:mm:ss.ffff"));
- Thread.Sleep(200);
- }
- }
-
- public static void CallbackHandler(IAsyncResult result) {
-
-
- AsyncExampleDelegate longRunningMethod =(AsyncExampleDelegate)result.AsyncState;
-
- DateTime completion = longRunningMethod.EndInvoke(result);
-
- Console.WriteLine("{0} : Callback example complete.", completion.ToString("HH:mm:ss.ffff"));
- }
[Nghean-Aptech st]