【问题标题】:Understanding Async Methods using C# 4.6.1使用 C# 4.6.1 了解异步方法
【发布时间】:2018-03-23 20:57:24
【问题描述】:

我有以下同步和异步方法

public static void Info(string message)
{
    if (_logTypes.Contains(InfoLogType))
    {
        string applicationName, methodName, className;
        ReflectClassAndMethod(out applicationName, out className, out methodName);
        Write(applicationName, "Info", className, methodName, message);
    }
}

public static async void InfoAsync(string message)
{
    if (_logTypes.Contains(InfoLogType))
    {
        string applicationName, methodName, className;
        ReflectClassAndMethod(out applicationName, out className, out methodName);
        await WriteAsync(applicationName, "Info", className, methodName, message);
    }
}

另外我的写法是这样的:

private static void Write(string applicationName, string logType, string className, string methodName, string message)
        {
         // construct me _event object
         var collection = Database.GetCollection<Event>(typeof(Event).Name);
         collection.InsertOne(_event);
    }

 private static Task WriteAsync(string applicationName, string logType, string className, string methodName, string message)
        {
         // construct my _event object
         var collection = Database.GetCollection<Event>(typeof(Event).Name);
          await collection.InsertOneAsync(_event);

        }

使用测试控制台应用程序,我将这些同步和异步方法称为如下:

 static void Main(string[] args)
    {
      Log.Info("Synchronous write");
      Log.InfoAsync(DateTime.Now.ToString(CultureInfo.InvariantCulture));
    }

这会将两个文档插入到我的 Mongo 集合中。但如果我将同步调用注释掉,则不会插入任何内容。

 static void Main(string[] args)
        {
          //Log.Info("Synchronous write");
          Log.InfoAsync(DateTime.Now.ToString(CultureInfo.InvariantCulture));
        }

这不会插入任何内容。这是为什么呢?

【问题讨论】:

  • 同意。我发现这个问题被正确地提出了,并且有一个实际的问题和可能的解决方案。 meta.stackexchange.com/questions/135/…
  • @smoksnes 但这些问题可以帮助声誉低于 2k 的用户。不适合像我这样的人... :(

标签: c# mongodb c#-4.0 asynchronous


【解决方案1】:

首先,我会尝试avoid async void InfoAsync

优先使用 async Task 方法而不是 async void 方法

你应该尝试这样的事情:

public static Task InfoAsync(string message)
{
    if (_logTypes.Contains(InfoLogType))
    {
        string applicationName, methodName, className;
        ReflectClassAndMethod(out applicationName, out className, out methodName);
        return WriteAsync(applicationName, "Info", className, methodName, message);
    }
}

这有几个好处。首先,没有理由等待WriteAsync。阅读更多关于它的信息here。它只会增加开销并可能对性能产生负面影响。其次,因为您返回的是 Task 而不是 void,所以调用者可以适当地等待它。

await 则在您的主目录中。由于它是主要的,因此您不能使用await,而是可以使用WaitAndUnwrapException()

static void Main(string[] args)
{
    //Log.Info("Synchronous write");
    var task = Log.InfoAsync(DateTime.Now.ToString(CultureInfo.InvariantCulture));
    task.WaitAndUnwrapException();

    // or
    // task.Wait();
    // Now the application won't finish before the async method is done.
}

如果我必须在我的任何其他方法中使用此 Log.InfoAsync 怎么办? WebAPP,哪些不是异步方法?

那么他们应该使用Info 而不是InfoAsync。如果可能,尽量使用async。如果不可能,那么您应该考虑是否真的使用async 方法添加任何内容。当你 await 它时,你仍然会阻塞线程,如果你正在使用 .Result there's a chance of deadlock。因此,在我看来,您想在同步上下文中使用 async 方法的情况很少。

更多有趣的阅读:Should Task.Wait be deprecated?

【讨论】:

  • 如果我必须在我的 WebAPP 的任何其他方法(不是异步方法)中使用此 Log.InfoAsync 怎么办?
  • 那么他们应该使用Info 而不是InfoAsync。如果可能,请尝试一直使用异步。如果不可能,那么您应该考虑是否真的使用异步方法添加任何内容。
  • 那么这意味着异步方法应该只从异步方法调用,而不是从同步方法调用?
  • @HaBo 问题是你没有向我们展示我假设的全部情况。您可以在同步调用中使用异步方法,但如果您想等待结果,它会阻塞。在您的示例/测试中,您必须等待/等待结果,因为应用程序将在完成之前终止。但是如果代码在网络服务器上运行 - 只需按照 smoksnes 描述的方式运行任务,但不要等待/等待结果。如果进程没有被关闭,则会进行日志记录。
【解决方案2】:

因为您的应用程序在记录器完成之前终止。

您可以直接await拨打电话:

static void Main(string[] args)
{
  //Log.Info("Synchronous write");
  await Log.InfoAsync(DateTime.Now.ToString(CultureInfo.InvariantCulture));
}

或者,因为这在 Main 中不起作用,请阅读:Can't specify the 'async' modifier on the 'Main' method of a console app

你可以直接Log.InfoAsync(DateTime.Now.ToString(CultureInfo.InvariantCulture)).Wait();


编辑

async Main 现在在使用 C# 7.1 时可用:https://github.com/dotnet/csharplang/blob/master/proposals/csharp-7.1/async-main.md

所以只需使用static async Task Main(string[] args)

【讨论】:

  • await 可能不是一个选项,因为我将在应用程序的各个部分使用此记录器,可以是同步或异步方法并尝试过。在同一个测试控制台应用程序上等待,没有区别。跨度>
  • 那么.Wait() 还是没有返回预期的结果?
  • 我必须将 InfoAsync 的返回类型更改为 TASK,然后我可以使用 .Wait() 并且 YES .Wait() 没有插入记录。
  • 你是对的。有用。但是现在 .Wait() 不是让它成为一个同步调用吗?
  • @HaBo,是的。您将阻塞线程,因此实际上没有理由在 Log.Info 上调用 Log.InfoAsync
猜你喜欢
  • 2023-04-07
  • 1970-01-01
  • 2016-02-23
  • 1970-01-01
  • 2013-04-15
  • 1970-01-01
  • 1970-01-01
  • 2016-09-28
  • 1970-01-01
相关资源
最近更新 更多