How to call asynchronous method from synchronous m

2018-12-31 03:14发布

I have a public async void Foo() method that I want to call from synchronous method. So far all I have seen from MSDN documentation is calling async methods via async methods, but my whole program is not built with async methods.

Is this even possible?

Here's one example of calling these methods from an asynchronous method: http://msdn.microsoft.com/en-us/library/hh300224(v=vs.110).aspx

Now I'm looking into calling these async methods from sync methods.

12条回答
看风景的人
2楼-- · 2018-12-31 03:22

async Main is now part of C# 7.2 and can be enabled in the projects advanced build settings.

For C# < 7.2, the correct way is:

static void Main(string[] args)
{
   MainAsync().GetAwaiter().GetResult();
}


static async Task MainAsync()
{
   /*await stuff here*/
}
查看更多
浮光初槿花落
3楼-- · 2018-12-31 03:25

Microsoft built an AsyncHelper (internal) class to run Async as Sync. The source looks like:

internal static class AsyncHelper
{
    private static readonly TaskFactory _myTaskFactory = new 
      TaskFactory(CancellationToken.None, 
                  TaskCreationOptions.None, 
                  TaskContinuationOptions.None, 
                  TaskScheduler.Default);

    public static TResult RunSync<TResult>(Func<Task<TResult>> func)
    {
        return AsyncHelper._myTaskFactory
          .StartNew<Task<TResult>>(func)
          .Unwrap<TResult>()
          .GetAwaiter()
          .GetResult();
    }

    public static void RunSync(Func<Task> func)
    {
        AsyncHelper._myTaskFactory
          .StartNew<Task>(func)
          .Unwrap()
          .GetAwaiter()
          .GetResult();
    }
}

The Microsoft.AspNet.Identity base classes only have Async methods and in order to call them as Sync there are classes with extension methods that look like (example usage):

public static TUser FindById<TUser, TKey>(this UserManager<TUser, TKey> manager, TKey userId) where TUser : class, IUser<TKey> where TKey : IEquatable<TKey>
{
    if (manager == null)
    {
        throw new ArgumentNullException("manager");
    }
    return AsyncHelper.RunSync<TUser>(() => manager.FindByIdAsync(userId));
}

public static bool IsInRole<TUser, TKey>(this UserManager<TUser, TKey> manager, TKey userId, string role) where TUser : class, IUser<TKey> where TKey : IEquatable<TKey>
{
    if (manager == null)
    {
        throw new ArgumentNullException("manager");
    }
    return AsyncHelper.RunSync<bool>(() => manager.IsInRoleAsync(userId, role));
}

For those concerned about the licensing terms of code, here is a link to very similar code (just adds support for culture on the thread) that has comments to indicate that it is MIT Licensed by Microsoft. https://github.com/aspnet/AspNetIdentity/blob/master/src/Microsoft.AspNet.Identity.Core/AsyncHelper.cs

查看更多
旧人旧事旧时光
4楼-- · 2018-12-31 03:30
public async Task<string> StartMyTask()
{
    await Foo()
    // code to execute once foo is done
}

static void Main()
{
     var myTask = StartMyTask(); // call your method which will return control once it hits await
     // now you can continue executing code here
     string result = myTask.Result; // wait for the task to complete to continue
     // use result

}

You read the 'await' keyword as "start this long running task, then return control to the calling method". Once the long-running task is done, then it executes the code after it. The code after the await is similar to what used to be CallBack methods. The big difference being the logical flow is not interrupted which makes it much easier to write and read.

查看更多
听够珍惜
5楼-- · 2018-12-31 03:31
var result = Task.Run(async () => await configManager.GetConfigurationAsync()).ConfigureAwait(false);

OpenIdConnectConfiguration config = result.GetAwaiter().GetResult();

Or use this:

var result=result.GetAwaiter().GetResult().AccessToken
查看更多
ら面具成の殇う
6楼-- · 2018-12-31 03:31

Those windows async methods have a nifty little method called AsTask(). You can use this to have the method return itself as a task so that you can manually call Wait() on it.

For example, on a Windows Phone 8 Silverlight application, you can do the following:

private void DeleteSynchronous(string path)
{
    StorageFolder localFolder = Windows.Storage.ApplicationData.Current.LocalFolder;
    Task t = localFolder.DeleteAsync(StorageDeleteOption.PermanentDelete).AsTask();
    t.Wait();
}

private void FunctionThatNeedsToBeSynchronous()
{
    // Do some work here
    // ....

    // Delete something in storage synchronously
    DeleteSynchronous("pathGoesHere");

    // Do other work here 
    // .....
}

Hope this helps!

查看更多
几人难应
7楼-- · 2018-12-31 03:35

I'm not 100% sure, but I believe the technique described in this blog should work in many circumstances:

You can thus use task.GetAwaiter().GetResult() if you want to directly invoke this propagation logic.

查看更多
登录 后发表回答