Tính năng C # await mới có tác dụng gì? [đóng cửa]


83

Bất cứ ai có thể giải thích những gì awaitchức năng làm?


1
Đây có phải là những gì bạn đang đề cập đến? Lập trình không đồng bộ trong C # 5.0 phần hai: Chờ đợi đến bao giờ?
jordanbtucker


Các ví dụ hay cũng có tại dotnetperls.com/async .
Miljen Mikic

Tôi không tin rằng câu hỏi này quá rộng hoặc nên được đóng lại. Nó hỏi một từ khóa có nghĩa là gì. (Có phải phiên bản trước đó khác không?)
Panzercrisis

Câu trả lời:


62

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();

2
Khi là nó dưới dạng C # của lời hứa: en.wikipedia.org/wiki/Futures_and_promises

12
Nghe rất giống với Thread.Join ().
Steve Guidi

10
Nhắc tôi về COMEFROM
Joel Spolsky

20
Để hoàn thiện, hãy thêm rằng đoạn mã trên phải được bao bọc trong một phương thức được tô điểm bằng một từ khóa không đồng bộ. Phương thức này sẽ ngay lập tức trả về ngay khi gặp từ khóa await đầu tiên trong đó.
Przemek

14
Theo cách nói của bạn: nó cho phép bạn "tạm dừng" phương thức, nhưng cần lưu ý rằng nó không tạm dừng hoặc chặn luồng.
Matt Crinklaw-Vogt

47

Về cơ bản, các từ khóa asyncawaitcho 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 returncâ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 returnchú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à asyncphả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.


31

Câu trả lời hiện được chấp nhận là sai lệch. awaitkhô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à asyncvà trả về Taskhoặc voidnếu bạn không quan tâm đến việc Taskphiê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()


1
Bạn cũng biết rằng nó sẽ chặn người gọi trong 3 giây trước khi nó thậm chí giao cho họ nhiệm vụ mà họ có thể làm 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.
Servy

1
@Servy vâng, đó là vấn đề. Để hiển thị tất cả các giai đoạn thực hiện. Đây không phải là một ví dụ trong thế giới thực.
Anri

7
@Servy bạn đang troll tôi à?
Anri

2
Không. Tôi đang cố gắng giúp bạn cải thiện câu trả lời của mình.
Servy

2
@ Anri ... Tôi thực sự đánh giá cao nỗ lực của bạn ở đây. Cảm ơn rất nhiều!!
Praveen Prajapati

11

Đố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ợ awaittừ 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 đó awaitvà 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 .


Gắn bó với sự tương tự giả mạo này (đã giúp tôi rất nhiều btw, vì vậy cảm ơn!), Bạn có nghĩa là "mọi thứ sau" là gì? Tất cả mọi thứ trong phạm vi của cùng một chức năng (phương thức) chỉ? Hoặc mọi thứ sau nó như trong bất kỳ thứ gì có thể đã được thêm vào ngăn xếp cuộc gọi?

2
"Mọi thứ sau" = phần còn lại của phương thức. Trình biên dịch viết lại một cách hiệu quả phần còn lại của phương thức dưới dạng gọi lại và điều khiển trả về ngay lập tức cho trình gọi của phương thức hiện tại.
Todd Menier

1
Brilliant Todd, một lần nữa cảm ơn vì lời giải thích của bạn. Tôi chắc chắn rằng có ích cho người khác.

-2

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");
        }
    }
}
Khi sử dụng trang web của chúng tôi, bạn xác nhận rằng bạn đã đọc và hiểu Chính sách cookieChính sách bảo mật của chúng tôi.
Licensed under cc by-sa 3.0 with attribution required.