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

这可能吗?

下面是一个从异步方法调用这些方法的例子: 演练:使用Async和Await访问Web (c#和Visual Basic)

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


当前回答

现在,您可以使用源生成器使用同步方法生成器库(nuget)创建方法的同步版本。

使用方法如下:

[Zomp.SyncMethodGenerator.CreateSyncVersion]
public async void FooAsync()

它会生成Foo方法,你可以同步调用。

其他回答

这是最简单的解决办法。我在网上的某个地方看到它,我不记得在哪里了,但我一直在成功地使用它。它不会死锁调用线程。

    void SynchronousFunction()
    {
        Task.Run(Foo).Wait();
    }

    string SynchronousFunctionReturnsString()
    {
        return Task.Run(Foo).Result;
    }

    string SynchronousFunctionReturnsStringWithParam(int id)
    {
        return Task.Run(() => Foo(id)).Result;
    }
var result = Task.Run(async () => await configManager.GetConfigurationAsync()).ConfigureAwait(false);

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

或者用这个:

var result=result.GetAwaiter().GetResult().AccessToken

微软构建了一个AsyncHelper(内部)类来作为同步运行Async。源代码如下所示:

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

identity基类只有Async方法,为了将它们调用为Sync,有一些类的扩展方法看起来像这样(示例用法):

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

对于那些关心代码许可条款的人,这里有一个非常相似的代码链接(只是在线程上增加了对文化的支持),其中有注释表明它是由微软授权的MIT。https://github.com/aspnet/AspNetIdentity/blob/master/src/Microsoft.AspNet.Identity.Core/AsyncHelper.cs

这与调用Task不一样吗?运行(async ()=> await AsyncFunc()).Result?说真的,微软现在不鼓励调用TaskFactory。StartNew,因为它们都是等效的,一个比另一个更可读。

绝对不是。

答案很简单

.Unwrap().GetAwaiter().GetResult() != .Result

首先

是任务。结果与.GetAwaiter.GetResult()?

其次,unwrap()会导致Task的设置不阻塞被包装的任务。

谁会问这个问题呢

这与调用Task不一样吗?运行(async ()=> await AsyncFunc()).GetAwaiter().GetResult()

那就得看情况了。

关于Task.Start(), Task.Run()和Task.Factory.StartNew()的使用

摘录:

的任务。Run使用taskcreateoptions。DenyChildAttach意思是子任务不能附加到父任务它使用TaskScheduler。默认值,这意味着在线程池中运行任务的线程将始终用于运行任务。 startnew使用TaskScheduler。Current表示当前线程的调度程序,它可能是TaskScheduler。违约但并非总是如此。

更多阅读:

指定同步上下文

ASP。NET Core SynchronizationContext

为了更加安全,像这样调用AsyncHelper不是更好吗?RunSync(async () => await AsyncMethod().ConfigureAwait(false));通过这种方式,我们告诉"内部"方法"请不要尝试同步到上层上下文并取消锁"

非常好的观点,就像大多数对象架构问题一样,这要视情况而定。

作为一个扩展方法,你想强迫绝对每个调用,还是让程序员使用该函数配置他们自己的异步调用?我可以看到call 3场景的用例;它很可能不是你在WPF中想要的,当然在大多数情况下是有意义的,但考虑到在ASP中没有上下文。Net Core如果你能保证它是ASP内部的。Net Core,那就无所谓了。

在尝试了几个小时不同的方法后,或多或少取得了成功,这就是我最终得到的结果。它在获取结果时不会以死锁结束,它还会获取并抛出原始异常,而不是被包装的异常。

private ReturnType RunSync()
{
  var task = Task.Run(async () => await myMethodAsync(agency));
  if (task.IsFaulted && task.Exception != null)
  {
    throw task.Exception;
  }

  return task.Result;
}

斯蒂芬·克利里的回答;

这种方法应该不会导致死锁(假设 ProblemMethodAsync不发送更新到UI线程或任何东西 像这样)。它假设可以在对象上调用ProblemMethodAsync 线程池线程,这并不总是这样。

https://blog.stephencleary.com/2012/07/dont-block-on-async-code.html

这就是方法;

线程池攻击与阻塞攻击类似的方法是 将异步工作卸载到线程池,然后阻塞 产生的任务。使用此黑客的代码看起来像下面的代码 如图7所示。 图7线程池攻击的代码 c#

public sealed class WebDataService : IDataService
{
  public string Get(int id)
  {
    return Task.Run(() => GetAsync(id)).GetAwaiter().GetResult();
  }
  public async Task<string> GetAsync(int id)
  {
    using (var client = new WebClient())
      return await client.DownloadStringTaskAsync(
      "https://www.example.com/api/values/" + id);
  }
}

Task的调用。Run在线程池上执行异步方法 线程。在这里,它将在没有上下文的情况下运行,从而避免 死锁。这种方法的一个问题是异步性 方法不能依赖于在特定上下文中执行。所以,它 不能使用UI元素或ASP。净HttpContext.Current。