Warning: file_get_contents(/data/phpspider/zhask/data//catemap/2/csharp/315.json): failed to open stream: No such file or directory in /data/phpspider/zhask/libs/function.php on line 167

Warning: Invalid argument supplied for foreach() in /data/phpspider/zhask/libs/tag.function.php on line 1116

Notice: Undefined index: in /data/phpspider/zhask/libs/function.php on line 180

Warning: array_chunk() expects parameter 1 to be array, null given in /data/phpspider/zhask/libs/function.php on line 181
如何在C#中从同步方法调用异步方法?_C#_Async Await - Fatal编程技术网

如何在C#中从同步方法调用异步方法?

如何在C#中从同步方法调用异步方法?,c#,async-await,C#,Async Await,我有一个公共异步void Foo()方法,我想从同步方法调用它。到目前为止,我在MSDN文档中看到的只是通过异步方法调用异步方法,但我的整个程序并不是用异步方法构建的 这可能吗 下面是从异步方法调用这些方法的一个示例: 现在我正在研究从sync方法调用这些异步方法。您可以从同步代码调用任何异步方法,也就是说,直到您需要等待它们,在这种情况下,它们也必须标记为async 正如很多人在这里建议的那样,您可以在同步方法中对生成的任务调用Wait()或Result,但最终在该方法中使用阻塞调用,这有点

我有一个
公共异步void Foo()
方法,我想从同步方法调用它。到目前为止,我在MSDN文档中看到的只是通过异步方法调用异步方法,但我的整个程序并不是用异步方法构建的

这可能吗

下面是从异步方法调用这些方法的一个示例:


现在我正在研究从sync方法调用这些异步方法。

您可以从同步代码调用任何异步方法,也就是说,直到您需要等待它们,在这种情况下,它们也必须标记为
async

正如很多人在这里建议的那样,您可以在同步方法中对生成的任务调用Wait()或Result,但最终在该方法中使用阻塞调用,这有点违背了异步的目的


如果你真的不能使你的方法
异步
,你也不想锁定同步方法,那么你必须使用回调方法,将它作为参数传递给任务中的ContinueWith方法。

你可以从同步代码调用任何异步方法,也就是说,直到你需要
等待
,在这种情况下,它们也必须标记为
async

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

}
正如很多人在这里建议的那样,您可以在同步方法中对生成的任务调用Wait()或Result,但最终在该方法中使用阻塞调用,这有点违背了异步的目的

如果您真的无法使方法
异步
,并且不想锁定同步方法,则必须使用回调方法,将其作为参数传递给任务上的ContinueWith方法。

公共异步任务StartMyTask()
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

}
{ 等福 //完成foo后要执行的代码 } 静态void Main() { var myTask=StartMyTask();//调用您的方法,该方法将在命中wait后返回控件 //现在您可以在这里继续执行代码了 string result=myTask.result;//等待任务完成后继续 //使用结果 }
您将'wait'关键字理解为“启动这个长时间运行的任务,然后将控制权返回给调用方法”。一旦长时间运行的任务完成,它就会在完成后执行代码。wait之后的代码类似于以前的回调方法。最大的区别在于逻辑流不会中断,这使得写和读更容易。

public async Task StartMyTask()
{
等福
//完成foo后要执行的代码
}
静态void Main()
{
var myTask=StartMyTask();//调用您的方法,该方法将在命中wait后返回控件
//现在您可以在这里继续执行代码了
string result=myTask.result;//等待任务完成后继续
//使用结果
}

您将'wait'关键字理解为“启动这个长时间运行的任务,然后将控制权返回给调用方法”。一旦长时间运行的任务完成,它就会在完成后执行代码。wait之后的代码类似于以前的回调方法。最大的区别在于逻辑流不会中断,这使得写和读更容易。

异步编程确实会通过代码库“增长”。它一直是。最好的解决办法是让它成长,但有时这是不可能的

我在我的库中编写了一些类型,用于处理部分异步的代码库。然而,没有一种解决方案在任何情况下都能奏效

解决方案A

如果您有一个简单的异步方法,不需要同步回其上下文,那么您可以使用
Task.waitandunwapexception

var task = MyAsyncMethod();
var result = task.WaitAndUnwrapException();
您不想使用
Task.Wait
Task.Result
,因为它们将异常包装在
aggregateeexception

仅当
MyAsyncMethod
未同步回其上下文时,此解决方案才适用。换句话说,
MyAsyncMethod
中的每个
wait
都应该以
configurewait(false)
结束。这意味着它无法更新任何UI元素或访问ASP.NET请求上下文

解决方案B

如果
MyAsyncMethod
确实需要同步回其上下文,则可以使用
AsyncContext.RunTask
提供嵌套上下文:

var result = AsyncContext.RunTask(MyAsyncMethod).Result;

*2014年4月14日更新:在库的最新版本中,API如下所示:

var result = AsyncContext.Run(MyAsyncMethod);

(在本例中可以使用
Task.Result
,因为
RunTask
将传播
Task
异常)

您可能需要使用
AsyncContext.RunTask
而不是
Task.waitandunwapexception
的原因是WinForms/WPF/SL/ASP.NET上发生了一种相当微妙的死锁可能性:

  • 同步方法调用异步方法,获得
    任务
  • 同步方法对
    任务执行阻塞等待
  • async
    方法使用
    await
    而不使用
    ConfigureAwait
  • 在这种情况下,
    任务
    无法完成,因为它只有在
    异步
    方法完成时才能完成;
    async
    方法无法完成,因为它正在尝试将其继续调度到
    SynchronizationContext
    ,WinForms/WPF/SL/ASP.NET将不允许继续运行,因为同步方法已在该上下文中运行
  • 这就是为什么在每个
    async
    方法中尽可能多地使用
    ConfigureAwait(false)
    是个好主意的原因之一

    解决方案C

    AsyncContext.RunTask
    不适用于所有场景。例如,如果
    async
    方法等待某个需要UI事件才能完成的操作,那么即使使用嵌套上下文,也会死锁。因为
    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();
        }
    }
    
    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));
    }
    
    static void Main(string[] args)
    {
       MainAsync().GetAwaiter().GetResult();
    }
    
    
    static async Task MainAsync()
    {
       /*await stuff here*/
    }
    
    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 
        // .....
    }
    
    using System;
    using System.Collections.Concurrent;
    using System.Collections.Generic;
    using System.Threading;
    using System.Threading.Tasks;
    
    namespace Microsoft.Threading
    {
        /// <summary>Provides a pump that supports running asynchronous methods on the current thread.</summary>
        public static class AsyncPump
        {
            /// <summary>Runs the specified asynchronous method.</summary>
            /// <param name="asyncMethod">The asynchronous method to execute.</param>
            public static void Run(Action asyncMethod)
            {
                if (asyncMethod == null) throw new ArgumentNullException("asyncMethod");
    
                var prevCtx = SynchronizationContext.Current;
                try
                {
                    // Establish the new context
                    var syncCtx = new SingleThreadSynchronizationContext(true);
                    SynchronizationContext.SetSynchronizationContext(syncCtx);
    
                    // Invoke the function
                    syncCtx.OperationStarted();
                    asyncMethod();
                    syncCtx.OperationCompleted();
    
                    // Pump continuations and propagate any exceptions
                    syncCtx.RunOnCurrentThread();
                }
                finally { SynchronizationContext.SetSynchronizationContext(prevCtx); }
            }
    
            /// <summary>Runs the specified asynchronous method.</summary>
            /// <param name="asyncMethod">The asynchronous method to execute.</param>
            public static void Run(Func<Task> asyncMethod)
            {
                if (asyncMethod == null) throw new ArgumentNullException("asyncMethod");
    
                var prevCtx = SynchronizationContext.Current;
                try
                {
                    // Establish the new context
                    var syncCtx = new SingleThreadSynchronizationContext(false);
                    SynchronizationContext.SetSynchronizationContext(syncCtx);
    
                    // Invoke the function and alert the context to when it completes
                    var t = asyncMethod();
                    if (t == null) throw new InvalidOperationException("No task provided.");
                    t.ContinueWith(delegate { syncCtx.Complete(); }, TaskScheduler.Default);
    
                    // Pump continuations and propagate any exceptions
                    syncCtx.RunOnCurrentThread();
                    t.GetAwaiter().GetResult();
                }
                finally { SynchronizationContext.SetSynchronizationContext(prevCtx); }
            }
    
            /// <summary>Runs the specified asynchronous method.</summary>
            /// <param name="asyncMethod">The asynchronous method to execute.</param>
            public static T Run<T>(Func<Task<T>> asyncMethod)
            {
                if (asyncMethod == null) throw new ArgumentNullException("asyncMethod");
    
                var prevCtx = SynchronizationContext.Current;
                try
                {
                    // Establish the new context
                    var syncCtx = new SingleThreadSynchronizationContext(false);
                    SynchronizationContext.SetSynchronizationContext(syncCtx);
    
                    // Invoke the function and alert the context to when it completes
                    var t = asyncMethod();
                    if (t == null) throw new InvalidOperationException("No task provided.");
                    t.ContinueWith(delegate { syncCtx.Complete(); }, TaskScheduler.Default);
    
                    // Pump continuations and propagate any exceptions
                    syncCtx.RunOnCurrentThread();
                    return t.GetAwaiter().GetResult();
                }
                finally { SynchronizationContext.SetSynchronizationContext(prevCtx); }
            }
    
            /// <summary>Provides a SynchronizationContext that's single-threaded.</summary>
            private sealed class SingleThreadSynchronizationContext : SynchronizationContext
            {
                /// <summary>The queue of work items.</summary>
                private readonly BlockingCollection<KeyValuePair<SendOrPostCallback, object>> m_queue =
                    new BlockingCollection<KeyValuePair<SendOrPostCallback, object>>();
                /// <summary>The processing thread.</summary>
                private readonly Thread m_thread = Thread.CurrentThread;
                /// <summary>The number of outstanding operations.</summary>
                private int m_operationCount = 0;
                /// <summary>Whether to track operations m_operationCount.</summary>
                private readonly bool m_trackOperations;
    
                /// <summary>Initializes the context.</summary>
                /// <param name="trackOperations">Whether to track operation count.</param>
                internal SingleThreadSynchronizationContext(bool trackOperations)
                {
                    m_trackOperations = trackOperations;
                }
    
                /// <summary>Dispatches an asynchronous message to the synchronization context.</summary>
                /// <param name="d">The System.Threading.SendOrPostCallback delegate to call.</param>
                /// <param name="state">The object passed to the delegate.</param>
                public override void Post(SendOrPostCallback d, object state)
                {
                    if (d == null) throw new ArgumentNullException("d");
                    m_queue.Add(new KeyValuePair<SendOrPostCallback, object>(d, state));
                }
    
                /// <summary>Not supported.</summary>
                public override void Send(SendOrPostCallback d, object state)
                {
                    throw new NotSupportedException("Synchronously sending is not supported.");
                }
    
                /// <summary>Runs an loop to process all queued work items.</summary>
                public void RunOnCurrentThread()
                {
                    foreach (var workItem in m_queue.GetConsumingEnumerable())
                        workItem.Key(workItem.Value);
                }
    
                /// <summary>Notifies the context that no more work will arrive.</summary>
                public void Complete() { m_queue.CompleteAdding(); }
    
                /// <summary>Invoked when an async operation is started.</summary>
                public override void OperationStarted()
                {
                    if (m_trackOperations)
                        Interlocked.Increment(ref m_operationCount);
                }
    
                /// <summary>Invoked when an async operation is completed.</summary>
                public override void OperationCompleted()
                {
                    if (m_trackOperations &&
                        Interlocked.Decrement(ref m_operationCount) == 0)
                        Complete();
                }
            }
        }
    }
    
    AsyncPump.Run(() => FooAsync(...));
    
    public class LogReader
    {
        ILogger _logger;
    
        public LogReader(ILogger logger)
        {
            _logger = logger;
        }
    
        public LogEntity GetLog()
        {
            Task<LogEntity> task = Task.Run<LogEntity>(async () => await GetLogAsync());
            return task.Result;
        }
    
        public async Task<LogEntity> GetLogAsync()
        {
            var result = await _logger.GetAsync();
            // more code here...
            return result as LogEntity;
        }
    }
    
    var result = Task.Run(async () => await configManager.GetConfigurationAsync()).ConfigureAwait(false);
    
    OpenIdConnectConfiguration config = result.GetAwaiter().GetResult();
    
    var result=result.GetAwaiter().GetResult().AccessToken
    
    MethodAsync().RunSynchronously()
    
    private ReturnType RunSync()
    {
      var task = Task.Run(async () => await myMethodAsync(agency));
      if (task.IsFaulted && task.Exception != null)
      {
        throw task.Exception;
      }
    
      return task.Result;
    }
    
    public static TResult RunSync<TResult>(Func<Task<TResult>> method)
    {
        var task = method();
        return task.GetAwaiter().GetResult();
    }
    
    public static void RunSync(Func<Task> method)
    {
        var task = method();
        task.GetAwaiter().GetResult();
    }
    
    RunSync(() => Foo());
    var result = RunSync(() => FooWithResult());
    
        void Synchronous Function()
        {
            Task.Run(Foo).Wait();
        }
    
        string SynchronousFunctionReturnsString()
        {
            return Task.Run(Foo).Result;
        }
    
        string SynchronousFunctionReturnsStringWithParam(int id)
        {
            return Task.Run(() => Foo(id)).Result;
        }