Bất cứ ai có thể giải thích những gì await
chức năng làm?
Bất cứ ai có thể giải thích những gì await
chức năng làm?
Câu trả lời:
Họ vừa nói về điều này tại PDC ngày hôm qua!
Await được sử dụng cùng với Tasks (lập trình song song) trong .NET. Đó là một từ khóa được giới thiệu trong phiên bản tiếp theo của .NET. Nó ít nhiều cho phép bạn "tạm dừng" việc thực thi một phương thức để đợi Tác vụ hoàn thành việc thực thi. Đây là một ví dụ ngắn gọn:
//create and run a new task
Task<DataTable> dataTask = new Task<DataTable>(SomeCrazyDatabaseOperation);
//run some other code immediately after this task is started and running
ShowLoaderControl();
StartStoryboard();
//this will actually "pause" the code execution until the task completes. It doesn't lock the thread, but rather waits for the result, similar to an async callback
// please so also note, that the task needs to be started before it can be awaited. Otherwise it will never return
dataTask.Start();
DataTable table = await dataTask;
//Now we can perform operations on the Task result, as if we're executing code after the async operation completed
listBoxControl.DataContext = table;
StopStoryboard();
HideLoaderControl();
Về cơ bản, các từ khóa async
và await
cho phép bạn chỉ định rằng việc thực thi một phương thức nên dừng lại ở tất cả các hoạt động sử dụng await
, đánh dấu các lệnh gọi phương thức không đồng bộ và sau đó tiếp tục khi hoạt động không đồng bộ hoàn tất. Điều này cho phép bạn gọi một phương thức trong luồng chính của ứng dụng và xử lý công việc phức tạp một cách không đồng bộ mà không cần xác định rõ ràng các luồng và tham gia hoặc chặn luồng chính của ứng dụng.
Hãy coi nó giống như một yield return
câu lệnh trong phương pháp tạo IEnumerable. Khi thời gian chạy chạm đến yield
, về cơ bản nó sẽ lưu trạng thái hiện tại của phương thức và trả về giá trị hoặc tham chiếu đang được tạo ra. Lần tiếp theo IEnumerator.MoveNext () được gọi trên đối tượng trả về (được tạo bên trong bởi thời gian chạy), trạng thái cũ của phương thức được khôi phục vào ngăn xếp và việc thực thi tiếp tục với dòng tiếp theo sau khi yield return
chúng ta không bao giờ rời khỏi phương pháp. Nếu không có từ khóa này, loại IEnumerator phải được xác định tùy chỉnh để lưu trữ trạng thái và xử lý các yêu cầu lặp lại, với các phương thức có thể trở nên RẤT phức tạp.
Tương tự, một phương thức được đánh dấu là async
phải có ít nhất một await
. Trên an await
, thời gian chạy sẽ lưu trạng thái của luồng hiện tại và ngăn xếp cuộc gọi, thực hiện cuộc gọi không đồng bộ và quay trở lại vòng lặp thông báo của thời gian chạy để xử lý thông báo tiếp theo và giữ cho ứng dụng phản hồi. Khi hoạt động không đồng bộ hoàn tất, ở cơ hội lập lịch tiếp theo, ngăn xếp cuộc gọi để lên hoạt động không đồng bộ được đẩy trở lại và tiếp tục như thể cuộc gọi là đồng bộ.
Vì vậy, hai từ khóa mới này về cơ bản đơn giản hóa việc mã hóa các quy trình không đồng bộ, giống như yield return
đơn giản hóa việc tạo bảng liệt kê tùy chỉnh. Với một vài từ khóa và một chút kiến thức nền tảng, bạn có thể bỏ qua tất cả các chi tiết khó hiểu và thường dễ xảy ra lỗi của mẫu không đồng bộ truyền thống. Điều này sẽ KHÔNG THỂ THIẾU trong khá nhiều ứng dụng GUI hướng sự kiện như Winforms, WPF of Silverlight.
Câu trả lời hiện được chấp nhận là sai lệch.
await
không tạm dừng bất cứ điều gì. Trước hết, nó chỉ có thể được sử dụng trong các phương thức hoặc lambdas được đánh dấu là async
và trả về Task
hoặc void
nếu bạn không quan tâm đến việc Task
phiên bản đang chạy trong phương thức này.
Đây là một minh họa:
internal class Program
{
private static void Main(string[] args)
{
var task = DoWork();
Console.WriteLine("Task status: " + task.Status);
Console.WriteLine("Waiting for ENTER");
Console.ReadLine();
}
private static async Task DoWork()
{
Console.WriteLine("Entered DoWork(). Sleeping 3");
// imitating time consuming code
// in a real-world app this should be inside task,
// so method returns fast
Thread.Sleep(3000);
await Task.Run(() =>
{
for (int i = 0; i < 10; i++)
{
Console.WriteLine("async task iteration " + i);
// imitating time consuming code
Thread.Sleep(1000);
}
});
Console.WriteLine("Exiting DoWork()");
}
}
Đầu ra:
Đã vào DoWork (). Ngủ 3
lần lặp nhiệm vụ không đồng bộ 0
Trạng thái tác vụ:
Chờ đợiForActivation Đang chờ ENTER
lần lặp tác vụ không đồng bộ 1 lần lặp
tác vụ không đồng bộ 2 lần lặp
tác vụ không đồng bộ 3
lần lặp tác vụ không đồng bộ 4
lần lặp tác vụ không đồng bộ 5
lần lặp tác vụ không đồng bộ 6
lần lặp tác vụ không đồng bộ 7
lần lặp tác vụ không đồng bộ 8
lần lặp tác vụ không đồng bộ 9
Thoát Làm việc()
await
? Có nghĩa là nếu điều này được gọi từ một chuỗi giao diện người dùng, nó sẽ chặn chuỗi giao diện người dùng trong 3 giây? Ý tưởng của mô hình này là tránh làm những việc như vậy.
Đối với bất kỳ ai mới làm quen với lập trình không đồng bộ trong .NET, đây là một phép tương tự (hoàn toàn giả mạo) trong một kịch bản mà bạn có thể quen thuộc hơn - AJAX gọi bằng JavaScript / jQuery. Một bài đăng jQuery AJAX đơn giản trông như thế này:
$.post(url, values, function(data) {
// AJAX call completed, do something with returned data here
});
Lý do chúng tôi xử lý kết quả trong một hàm gọi lại là vì vậy chúng tôi không chặn luồng hiện tại trong khi chờ lệnh gọi AJAX trả về. Chỉ khi phản hồi sẵn sàng thì lệnh gọi lại mới được kích hoạt, giải phóng luồng hiện tại để làm những việc khác trong thời gian có ý nghĩa.
Bây giờ, nếu JavaScript hỗ trợ await
từ khóa (tất nhiên là nó chưa ( chưa )), bạn có thể đạt được điều tương tự với điều này:
var data = await $.post(url, values);
// AJAX call completed, do something with returned data here
Rõ ràng hơn rất nhiều, nhưng có vẻ như chúng tôi đã giới thiệu mã chặn đồng bộ. Nhưng trình biên dịch JavaScript (giả mạo) sẽ lấy mọi thứ sau đó await
và kết nối nó thành một lệnh gọi lại, vì vậy trong thời gian chạy, ví dụ thứ hai sẽ hoạt động giống như ví dụ đầu tiên.
Có vẻ như nó không giúp bạn tiết kiệm nhiều công việc, nhưng khi nói đến những thứ như xử lý ngoại lệ và bối cảnh đồng bộ hóa, trình biên dịch thực sự đang làm rất nhiều việc nặng nhọc cho bạn. Để biết thêm, tôi muốn giới thiệu Câu hỏi thường gặp sau loạt blog của Stephen Cleary .
Nếu tôi phải triển khai nó trong Java, nó sẽ trông giống như sau:
/**
* @author Ilya Gazman
*/
public abstract class SynchronizedTask{
private ArrayList<Runnable> listeners = new ArrayList<Runnable>();
private static final ThreadPoolExecutor threadPoolExecutor = new ThreadPoolExecutor(6, 6, 0, TimeUnit.MILLISECONDS, new ArrayBlockingQueue<Runnable>(1000));
public final void await(Runnable listener){
synchronized (this) {
listeners.add(listener);
}
}
public void excecute(){
onExcecute();
for (int i = listeners.size() - 1; i >= 0; i--) {
Runnable runnable;
synchronized (this) {
runnable = listeners.remove(i);
}
threadPoolExecutor.execute(runnable);
}
}
protected abstract void onExcecute();
}
Ứng dụng của bạn sẽ sử dụng nó như thế này:
public class Test{
private Job job = new Job();
public Test() {
craeteSomeJobToRunInBackground();
methode1();
methode2();
}
private void methode1(){
System.out.println("Running methode 1");
job.await(new Runnable() {
@Override
public void run() {
System.out.println("Continue to running methode 1");
}
});
}
private void methode2(){
System.out.println("Running methode 2");
}
private void craeteSomeJobToRunInBackground() {
new Thread(new Runnable() {
@Override
public void run() {
job.excecute();
}
}).start();
}
private class Job extends SynchronizedTask{
@Override
protected void onExcecute() {
try {
Thread.sleep(1000);
}
catch (InterruptedException e) {
e.printStackTrace();
}
System.out.println("Job is done");
}
}
}