How to call asynchronous method from synchronous method in C#?

后端 未结 15 1032
星月不相逢
星月不相逢 2020-11-21 06:27

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

相关标签:
15条回答
  • 2020-11-21 06:46

    Here is the simplest solution. I saw it somewhere on the Internet, I didn't remember where, but I have been using it successfully. It will not deadlock the calling thread.

        void Synchronous Function()
        {
            Task.Run(Foo).Wait();
        }
    
        string SynchronousFunctionReturnsString()
        {
            return Task.Run(Foo).Result;
        }
    
        string SynchronousFunctionReturnsStringWithParam(int id)
        {
            return Task.Run(() => Foo(id)).Result;
        }
    
    0 讨论(0)
  • 2020-11-21 06:47
    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.

    0 讨论(0)
  • 2020-11-21 06:48
    var result = Task.Run(async () => await configManager.GetConfigurationAsync()).ConfigureAwait(false);
    
    OpenIdConnectConfiguration config = result.GetAwaiter().GetResult();
    

    Or use this:

    var result=result.GetAwaiter().GetResult().AccessToken
    
    0 讨论(0)
  • 2020-11-21 06:48

    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!

    0 讨论(0)
  • 2020-11-21 06:49

    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

    0 讨论(0)
  • 2020-11-21 06:53

    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*/
    }
    

    You'll see this used in a lot of Microsoft documentation, for example: https://docs.microsoft.com/en-us/azure/service-bus-messaging/service-bus-dotnet-how-to-use-topics-subscriptions

    0 讨论(0)
提交回复
热议问题