使用Async-Await的真正优势是什么?

7

之前我发布了一个关于在客户端或服务端应用 Async-Await 的问题,你需要先阅读那个问题再来看这个问题,因为这两个问题是紧密相关的。问题链接:https://dev59.com/h2jWa4cB1Zd3GeqPvveY

根据答案,我已经测试了 C# 4.0(TPL)和 C# 5.0(Async - Await)的代码。我使用异步和同步方法调用服务,并比较了每种情况下使用的线程数。

以下是我用于测试资源使用情况的代码:

主要方法

List<Task<string>> tasksList = new List<Task<string>>();
List<int> asyncThreads = new List<int>();
List<int> tplThreads = new List<int>();
Stopwatch watch = new Stopwatch();
watch.Start();

// Call the Async version of the method
for (int i = 0; i < 500; i++)
{
    tasksList.Add(GetNameFromServiceAsync("Input" + i.ToString(), asyncThreads));
}

Task.WaitAll(tasksList.ToArray());

watch.Stop();

foreach (var item in asyncThreads.Distinct())
{
    Console.WriteLine(item);
}

Console.WriteLine("(C# 5.0)Asynchrony Total Threads = " + asyncThreads.Distinct().Count());
Console.WriteLine(watch.ElapsedMilliseconds.ToString());

watch.Restart();

tasksList.Clear();

// Call the normal method
for (int i = 0; i < 500; i++)
{
    tasksList.Add(GetNameFromService("Input" + i.ToString(), tplThreads));
}

Task.WaitAll(tasksList.ToArray());

watch.Stop();

foreach (var item in tplThreads.Distinct())
{
    Console.WriteLine(item);
}

Console.WriteLine("(C# 4.0)TPL Total Threads" + tplThreads.Distinct().Count());

Console.WriteLine(watch.ElapsedMilliseconds.ToString());

异步和同步调用服务

static async Task<string> GetNameFromServiceAsync(string name, List<int> threads)
{
  Console.WriteLine(" Start Current Thread : " + System.Threading.Thread.CurrentThread.ManagedThreadId);
    var task = await client.GetNameAsync(name);
    threads.Add(System.Threading.Thread.CurrentThread.ManagedThreadId);
   // Console.WriteLine("End GetNameFromServiceAsync Current Thread : " + System.Threading.Thread.CurrentThread.ManagedThreadId);
    return task;
}

static Task<string> GetNameFromService(string name, List<int> threads)
{

    var task = Task<string>.Factory.StartNew(() =>
        {
            threads.Add(System.Threading.Thread.CurrentThread.ManagedThreadId);
         //   Console.WriteLine("GetNameFromService Current Thread : " + System.Threading.Thread.CurrentThread.ManagedThreadId);
            return client.GetName(name);
        });

    return task;
}

现在我已经研究了答案,并得出以下结果:
  • 如果我对服务进行500次调用,它只使用4-5个线程。
  • TPL调用会产生大约44-45个线程。
  • 异步调用的时间约为17-18秒
  • TPL调用的时间约为42-45秒。
我希望能够得到一些关于我的发现的反馈,以便对其他社区成员也有用。这是我之前问题的答案吗?
编辑:
Q. 我的观察结论是,如果我们使用Async-Await而不是TPL的Task.Factory.startNew,那么它将消耗更少的线程。这正确吗?如果不是,那么应该如何进行比较?
Q. 因为我正在学习async-await,所以我想通过某种比较和可靠的代码来证明它的价值。

8
你的问题是什么? - Stephen Cleary
通过使用async-await,我们可以获得内存优势(使用更少的资源)。这是我从之前的问题中得到的或者我理解的,我想知道这是否是它的答案。我想在使用async-await的可扩展性优势的背景下询问。这是否符合我的结论? - Learner
一个重要的优点是显而易见的,即当用户使用同步代码时,他们不能说遇到了异步问题。 - Furqan Safdar
2
微基准测试通常不能很好地评估这类事情,在我看来。 - James Manning
1个回答

17

与同步代码相比,客户端async通常可以提高响应性,但会略微增加内存使用成本。

与同步代码相比,服务器端async通常通过减少内存/线程使用来提高可伸缩性。这些优势也适用于客户端async(与多线程代码相比)。

这两者都是极端概括,肯定有例外情况。

更新:

我观察得出,如果我们使用Async-Await ...,那么它将消耗较少的线程。

asyncawait使异步编码更易于维护。它们本身与创建线程无关。然而,它们经常与Task.Run(或Task.Factory.StartNew)一起用于创建后台任务。

我正在学习异步-等待,我想通过某种比较和坚实的代码来证明其价值。

asyncawait是编译器转换。它们使编写异步程序更容易 - 就是这样。

如果与同步代码进行比较,则通常会看到改进的响应性和/或可伸缩性。如果与现有的异步代码进行比较,则通常会略微降低效率,但在代码可维护性方面会更加优秀。


网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接