如何使一个异步方法返回一个值?


76

我知道如何制作异步方法,但是说我有一个方法需要大量工作,然后返回布尔值?

如何在回调中返回布尔值?

澄清

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

我希望能够使此异步。

Answers:


58

有几种方法可以做...最简单的是让async方法也执行后续操作。另一种流行的方法是传递回调,即

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

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

另一种方法是在异步操作完成后引发一个事件(结果存储在event-args数据中)。

另外,如果您使用的是TPL,则可以使用ContinueWith

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

4
回调方法的简单示例
DRapp

136

C#5.0中,您可以将方法指定为

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

bool result = await doAsyncOperation();

25
对于任何在乎的人,要获得您要使用的结果bool result = await doAsyncOperation();
Gordon Tucker 2013年

4
每当您要使用“ await”关键字时,它都必须位于已至少标记为“ async”的方法内。我最初虽然只需要以这种方式标记worker方法。例如,即使您的调用者方法不会返回任何内容,您也必须将其命名为“ async void MyCallerMethod”
Mike K

5
@KingOfHypocritesreturn await Task.FromResult(true)将取消该警告。
Bojan Komazec 2015年

2
除非您await在方法主体中进行操作,否则这实际上不会异步执行任何操作。返回Task.FromResult(true)并没有改变。方法主体在调用者的线程上同步运行,直到第一次等待。
Drew Noakes

2
在我看来,要使其正常工作,“ bool result = ...”行也必须位于异步方法内,因此我认为这并不能真正回答问题
nuander

4

可能最简单的方法是创建一个委托,然后创建BeginInvoke,然后在将来的某个时间等待,然后创建一个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是一个WaitHandle。它没有WaitOne()方法。您必须将其放低到它容纳的所有内容,以等待完成。
the_drow

1
@the_drow:您可能想看一下以下示例:msdn.microsoft.com/en-us/library/…。还可以查看该WaitHandle.WaitOne方法的文档:msdn.microsoft.com/en-us/library/58195swd.aspx
Jim Mischel

2
或者,您也可以将该代码粘贴到C#程序中并进行测试。它似乎为我工作。
Jim Mischel

3

使用BackgroundWorker。它将允许您在完成时获取回调并跟踪进度。您可以将事件参数上的Result值设置为结果值。

    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;
    }

后台工作者在这里适得其反。您不知道AutoResetEvent / ManualResetEvent吗?
the_drow

1
@the_drow我不同意;BackgroundWorker和RunWorkerCompleted事件在这里是一个很好的方法
Marc Gravell

@the_drow-不,我不知道。我将了解我可以学到的东西。但是,如果您能解释一下为什么您认为这适得其反,我想了解一下。
NerdFury 2011年

这会适得其反,因为您为真正不需要这种代码的东西编写了太多代码。当您有一个长时间运行的过程向UI报告时,您将执行后台工作。c#中的异步方法不需要报告进度,只需完成,其他人就可以等待它的完成(这就是Auto / MenualResetEvent的目的)。异步方法模式是用于异步调用方法的已知习语。这里唯一有效的方法是我建议的或@Marc建议的方法。
the_drow 2011年

@MarcGravell:并非每件事都是钉子。它可能会起作用。从语义上来说是不正确的。
the_drow

1

也许您可以尝试像这样开始BeginInvoke指向您的方法的委托:



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

然后,使用您以AsyncCallback发送的方法中的值继续。


-3

您应该使用异步方法的EndXXX返回该值。EndXXX应该等到使用IAsyncResult的WaitHandle得到结果,然后再返回该值。

By using our site, you acknowledge that you have read and understand our Cookie Policy and Privacy Policy.
Licensed under cc by-sa 3.0 with attribution required.