Làm cách nào để đặt một Phương thức không đồng bộ trả về một giá trị?


76

Tôi biết cách tạo các phương thức Async nhưng nói rằng tôi có một phương thức thực hiện nhiều công việc sau đó trả về giá trị boolean?

Làm cách nào để trả về giá trị boolean trên lệnh gọi lại?

Làm rõ :

public bool Foo(){
    Thread.Sleep(100000); // Do work
    return true;
}

Tôi muốn có thể làm cho điều này không đồng bộ.

Câu trả lời:


58

Có một số cách để làm điều đó ... đơn giản nhất là phương thức async cũng thực hiện thao tác tiếp theo. Một cách tiếp cận phổ biến khác là chuyển một lệnh gọi lại, tức là

void RunFooAsync(..., Action<bool> callback) {
     // do some stuff
     bool result = ...

     if(callback != null) callback(result);
}

Một cách tiếp cận khác sẽ là tăng một sự kiện (với kết quả là dữ liệu event-args) khi hoạt động không đồng bộ hoàn tất.

Ngoài ra, nếu bạn đang sử dụng TPL, bạn có thể sử dụng ContinueWith:

Task<bool> outerTask = ...;
outerTask.ContinueWith(task =>
{
    bool result = task.Result;
    // do something with that
});

4
Đẹp đơn giản ví dụ về cách tiếp cận callback
DRapp

138

Từ C # 5.0 , bạn có thể chỉ định phương thức là

public async Task<bool> doAsyncOperation()
{
    // do work
    return true;
}

bool result = await doAsyncOperation();

25
Đối với bất cứ ai khác mà quan tâm, để có được những kết quả mà bạn muốn sử dụngbool result = await doAsyncOperation();
Gordon Tucker

4
Bất cứ khi nào bạn định sử dụng từ khóa "await", nó cần phải ở bên trong một phương thức đã được đánh dấu, tối thiểu là "async". Tôi ban đầu mặc dù chỉ có phương thức worker phải được đánh dấu theo cách này. Ví dụ ngay cả khi phương pháp gọi của bạn sẽ không trở lại bất cứ điều gì bạn phải đặt tên nó là "async trống MyCallerMethod"
Mike K

5
@KingOfHypocrites return await Task.FromResult(true)sẽ loại bỏ cảnh báo đó.
Bojan Komazec

2
Điều này sẽ không thực sự làm bất cứ điều gì không đồng bộ trừ khi bạn có awaitmột cái gì đó trong thân phương thức. Quay trở lại Task.FromResult(true)không thay đổi điều đó. Phần thân phương thức chạy đồng bộ trên luồng của người gọi cho đến lần chờ đợi đầu tiên.
Drew Noakes

2
Vẻ với tôi như để làm việc này là "kết quả bool = ..." dòng cũng phải nằm trong một phương pháp async, vì vậy tôi không nghĩ rằng đây thực sự trả lời câu hỏi
nuander

4

Có lẽ cách đơn giản nhất để làm điều đó là tạo một đại diện và sau đó BeginInvoke, tiếp theo là chờ một lúc nào đó trong tương lai, và một EndInvoke.

public bool Foo(){
    Thread.Sleep(100000); // Do work
    return true;
}

public SomeMethod()
{
    var fooCaller = new Func<bool>(Foo);
    // Call the method asynchronously
    var asyncResult = fooCaller.BeginInvoke(null, null);

    // Potentially do other work while the asynchronous method is executing.

    // Finally, wait for result
    asyncResult.AsyncWaitHandle.WaitOne();
    bool fooResult = fooCaller.EndInvoke(asyncResult);

    Console.WriteLine("Foo returned {0}", fooResult);
}

AsyncWaitHandle là một WaitHandle. Nó không có phương thức WaitOne (). Bạn phải bỏ nó xuống bất cứ thứ gì nó giữ để chờ hoàn thành.
the_drow

1
@the_drow: Bạn có thể muốn xem ví dụ này: msdn.microsoft.com/en-us/library/… . Ngoài ra, hãy xem tài liệu cho WaitHandle.WaitOnephương pháp: msdn.microsoft.com/en-us/library/58195swd.aspx
Jim Mischel

2
Hoặc bạn có thể chỉ cần dán mã đó vào chương trình C # và kiểm tra nó. Nó dường như làm việc cho tôi.
Jim Mischel

3

Sử dụng BackgroundWorker. Nó sẽ cho phép bạn nhận được các cuộc gọi lại khi hoàn thành và cho phép bạn theo dõi tiến trình. Bạn có thể đặt giá trị Kết quả trên các đối số của sự kiện thành giá trị kết quả.

    public void UseBackgroundWorker()
    {
        var worker = new BackgroundWorker();
        worker.DoWork += DoWork;
        worker.RunWorkerCompleted += WorkDone;
        worker.RunWorkerAsync("input");
    }

    public void DoWork(object sender, DoWorkEventArgs e)
    {
        e.Result = e.Argument.Equals("input");
        Thread.Sleep(1000);
    }

    public void WorkDone(object sender, RunWorkerCompletedEventArgs e)
    {
        var result = (bool) e.Result;
    }

Một nhân viên nền đang phản tác dụng ở đây. Bạn không biết AutoResetEvent / ManualResetEvent?
the_drow

1
@the_drow Tôi không đồng ý với điều đó; một BackgroundWorker và sự kiện RunWorkerCompleted là một cách tiếp cận hoàn toàn tốt ở đây
Marc Gravell

@the_drow - không, tôi không. Tôi sẽ xem những gì tôi có thể tìm hiểu về nó. Nhưng nếu bạn có thể giải thích tại sao bạn cho rằng điều này phản tác dụng, tôi muốn hiểu.
NerdFury

Nó phản tác dụng vì bạn đang viết mã quá nhiều cho một thứ không thực sự cần đến loại mã đó. Nhân viên nền là những gì bạn làm khi bạn có một quá trình chạy dài báo cáo cho giao diện người dùng. Phương thức không đồng bộ trong c # không cần báo cáo tiến độ, chỉ hoàn thành vì những người khác có thể đợi nó hoàn thành (đó là những gì Auto / MenualResetEvent dành cho). Mẫu phương thức không đồng bộ là một thành ngữ đã biết để gọi một phương thức không đồng bộ. Các cách tiếp cận hợp lệ duy nhất ở đây là những gì tôi đã đề xuất hoặc những gì @Marc đề xuất.
the_drow

@MarcGravell: Không phải mọi thứ đều là đinh. Nó có thể hoạt động. Nó không chính xác về mặt ngữ nghĩa.
the_drow

1

Có lẽ bạn có thể thử BeginInvoke một đại biểu trỏ đến phương thức của bạn như sau:



    delegate string SynchOperation(string value);

    class Program
    {
        static void Main(string[] args)
        {
            BeginTheSynchronousOperation(CallbackOperation, "my value");
            Console.ReadLine();
        }

        static void BeginTheSynchronousOperation(AsyncCallback callback, string value)
        {
            SynchOperation op = new SynchOperation(SynchronousOperation);
            op.BeginInvoke(value, callback, op);
        }

        static string SynchronousOperation(string value)
        {
            Thread.Sleep(10000);
            return value;
        }

        static void CallbackOperation(IAsyncResult result)
        {
            // get your delegate
            var ar = result.AsyncState as SynchOperation;
            // end invoke and get value
            var returned = ar.EndInvoke(result);

            Console.WriteLine(returned);
        }
    }

Sau đó, sử dụng giá trị trong phương thức bạn đã gửi dưới dạng AsyncCallback để tiếp tục ..


-3

Bạn nên sử dụng EndXXX của phương thức không đồng bộ để trả về giá trị. EndXXX nên đợi cho đến khi có kết quả bằng cách sử dụng WaitHandle của IAsyncResult và trả về với giá trị.

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.