Warning: file_get_contents(/data/phpspider/zhask/data//catemap/2/.net/20.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# 如果能够使用Task.run异步运行任何方法,那么.Net Framework中的*异步方法的用途是什么?_C#_.net_Multithreading_Asynchronous_Async Await - Fatal编程技术网

C# 如果能够使用Task.run异步运行任何方法,那么.Net Framework中的*异步方法的用途是什么?

C# 如果能够使用Task.run异步运行任何方法,那么.Net Framework中的*异步方法的用途是什么?,c#,.net,multithreading,asynchronous,async-await,C#,.net,Multithreading,Asynchronous,Async Await,简短问题: 为什么.Net Framework添加了很多*异步版本的方法,而不是开发人员只使用Task.Run异步运行同步方法 详细问题: 我理解异步的概念 我了解任务 我知道async/await关键字 我知道.Net Framework中的*异步方法做什么 我不明白的是库中*异步方法的用途 假设您有两行代码: F1(); F2(); 关于数据/控制流,只有两种情况: F2需要在F1完成后执行 F2无需等待F1完成 我没有看到任何其他案例。我不认为有任何需要知道执行某些功能(除了UI

简短问题:

为什么.Net Framework添加了很多*异步版本的方法,而不是开发人员只使用
Task.Run
异步运行同步方法

详细问题:

  • 我理解异步的概念
  • 我了解
    任务
  • 我知道async/await关键字
  • 我知道.Net Framework中的*异步方法做什么
我不明白的是库中*异步方法的用途

假设您有两行代码:

F1();
F2();
关于数据/控制流,只有两种情况:

  • F2
    需要在
    F1
    完成后执行
  • F2
    无需等待
    F1
    完成
我没有看到任何其他案例。我不认为有任何需要知道执行某些功能(除了UI)的具体线程的一般需求。线程中代码的基本执行模式是同步的。并行性需要多个线程。异步性基于并行性和代码重新排序。但基础仍然是同步的

F1
的工作负载很小时,这种差异并不重要。但是当A需要很多时间才能完成时,我们可能需要查看情况,如果
F2
不需要等待
F1
完成,我们可以与
F2
并行运行
F1

很久以前,我们使用线程/线程池实现了这一点。现在我们有了
任务

如果我们想并行运行
F1
F2
,我们可以编写:

var task1 = Task.Run(F1);
F2();
任务很酷,我们可以在最终需要完成任务的地方使用
wait

到目前为止,我认为没有必要创建
F1Async()
方法

现在,让我们来看一些特殊情况。 我看到的唯一真正的特例是UI。UI线程是特殊的,它会使UI冻结,这是很糟糕的。 在我看来,Microsoft建议我们将UI事件处理程序标记为异步。标记方法
async
意味着我们可以使用
wait
关键字基本上在另一个线程上安排繁重的处理,并释放UI线程,直到处理完成

我不明白的是,为什么我们需要任何*异步方法来等待它们。我们总是可以编写
等待任务。运行(F1)。为什么我们需要
F1Async

您可以说*异步方法使用了一些特殊的魔法(比如处理外部信号),使它们比同步方法更有效。问题是我不认为这是事实

例如,让我们看一下
流.ReadAsync
。如果您查看源代码,
ReadAsync
只是浪费了几百行代码来创建一个只调用synchronous
Read
方法的任务。那我们为什么需要它呢?为什么不直接使用
任务。使用
流运行
。读取

这就是为什么我不理解通过创建同步方法的平凡*异步副本来扩充库的必要性。MS甚至可以添加语法糖,这样我们就可以编写
await async Stream.Read
而不是
await Stream.ReadAsync
Task.Run(Stream.Read)

现在您可能会问“为什么不让*异步方法成为唯一的方法,并删除同步方法?”。如前所述,基本代码执行模式是同步的。异步运行同步方法很容易,但另一种方式不行

那么,如果能够使用Task.run异步运行任何方法,.Net Framework中的*Async方法的用途是什么?

另外,如果UI的非冻结功能非常重要,为什么不在默认情况下异步运行处理程序,以防止冻结

无线程参数:

回答这个问题的人似乎暗示*Async方法的优点是它们效率高,因为它们不创建新线程。问题是我没有看到这种行为。并行异步任务的行为与我所想的一样——为每个并行任务创建(或从线程池中获取)一个线程(但并非所有任务都并行执行)

以下是我的测试代码:

using System;
using System.Collections.Generic;
using System.Diagnostics;
using System.Linq;
using System.Net.Http;
using System.Threading;
using System.Threading.Tasks;

namespace ConsoleApplication32167 {
    class Program {
        static async Task TestAsync() {
            var httpClient = new HttpClient() { Timeout = TimeSpan.FromMinutes(20) };

            var tasks = Enumerable.Range(1, 100).Select((i) =>
                httpClient.GetStringAsync("http://localhost/SlowWebsite/"));

            Console.WriteLine("Threads before completion: " + Process.GetCurrentProcess().Threads.Count);

            await Task.WhenAll(tasks);

            Console.WriteLine("Threads after completion: " + Process.GetCurrentProcess().Threads.Count);
        }

        static void Main(string[] args) {
            Console.WriteLine("Threads at start: " + Process.GetCurrentProcess().Threads.Count);

            var timer = new Stopwatch();
            timer.Start();

            var testTask = TestAsync();

            var distinctThreadIds = new HashSet<int>();
            while (!testTask.IsCompleted) {
                var threadIds = Process.GetCurrentProcess().Threads.OfType<ProcessThread>().Select(thread => thread.Id).ToList();
                distinctThreadIds.UnionWith(threadIds);
                Console.WriteLine("Current thread count: {0}; Cumulative thread count: {1}.", threadIds.Count, distinctThreadIds.Count);
                Thread.Sleep(250);
            }

            testTask.Wait();

            Console.WriteLine(timer.Elapsed);
            Console.ReadLine();
        }
    }
}
这意味着:

  • 在异步任务执行过程中创建了61个新线程
  • 新活动线程的峰值数为21
  • 执行时间增加了10倍(10分钟而不是1分钟)。这是由本地IIS限制造成的
将方法标记为async意味着我们可以使用await关键字基本上在另一个线程上调度繁重的处理,并释放UI线程,直到处理完成

这根本不是
async
的工作方式。看我的

您可以说*异步方法使用了一些特殊的魔法(比如处理外部信号),使它们比同步方法更有效。问题是我不认为这是事实

在纯异步代码中(正如我在博客上解释的)。事实上,在设备驱动程序级别,所有(非平凡)I/O都是异步的。同步API(在操作系统级别)是自然异步API之上的抽象层

例如,让我们看一下Stream.ReadAsync

是一种不寻常的情况。作为基类,它必须尽可能防止破坏更改。因此,当他们添加virtual
ReadAsync
方法时,他们必须添加一个默认实现。此实现必须使用非理想的实现(
Task.Run
),这是不幸的。在理想情况下,
ReadAsync
将是(或调用)抽象异步的
Current thread count: 4; Cumulative thread count: 4.
....
Current thread count: 25; Cumulative thread count: 25.
....
Current thread count: 7; Cumulative thread count: 63.
Current thread count: 9; Cumulative thread count: 65.
00:10:01.9981006
ThreadPool.SetMaxThreads(MAX_REQS * 2, MAX_REQS * 2);
ThreadPool.SetMinThreads(MAX_REQS, MAX_REQS);
using System;
using System.Diagnostics;
using System.Threading;
using System.Threading.Tasks;
using System.Linq;
using System.Collections.Generic;
using System.Net;

namespace Console_21690385
{
    class Program
    {
        const int MAX_REQS = 200;

        // implement GetStringAsync
        static async Task<string> GetStringAsync(string url)
        {
            using (var response = await WebRequest.Create(url).GetResponseAsync())
            using (var stream = response.GetResponseStream())
            using (var reader = new System.IO.StreamReader(stream))
            {
                return await reader.ReadToEndAsync();
            }
        }

        // test using GetStringAsync
        static async Task TestWithGetStringAsync()
        {
            var tasks = Enumerable.Range(1, MAX_REQS).Select((i) =>
                GetStringAsync("http://www.bing.com/search?q=item1=" + i));

            Console.WriteLine("Threads before completion: " + Process.GetCurrentProcess().Threads.Count);

            await Task.WhenAll(tasks);

            Console.WriteLine("Threads after completion: " + Process.GetCurrentProcess().Threads.Count);
        }

        // implement GetStringSync
        static string GetStringSync(string url)
        {
            using (var response = WebRequest.Create(url).GetResponse())
            using (var stream = response.GetResponseStream())
            using (var reader = new System.IO.StreamReader(stream))
            {
                return reader.ReadToEnd();
            }
        }

        // test using GetStringSync
        static async Task TestWithGetStringSync()
        {
            var tasks = Enumerable.Range(1, MAX_REQS).Select((i) =>
                Task.Factory.StartNew(
                    () => GetStringSync("http://www.bing.com/search?q=item1=" + i),
                    CancellationToken.None, TaskCreationOptions.PreferFairness, TaskScheduler.Default));

            Console.WriteLine("Threads before completion: " + Process.GetCurrentProcess().Threads.Count);

            await Task.WhenAll(tasks);

            Console.WriteLine("Threads after completion: " + Process.GetCurrentProcess().Threads.Count);
        }

        // run either of the tests
        static void RunTest(Func<Task> runTest)
        {
            Console.WriteLine("Threads at start: " + Process.GetCurrentProcess().Threads.Count);

            var stopWatch = new Stopwatch();
            stopWatch.Start();

            var testTask = runTest();

            while (!testTask.IsCompleted)
            {
                Console.WriteLine("Currently threads: " + Process.GetCurrentProcess().Threads.Count);
                Thread.Sleep(1000);
            }
            Console.WriteLine("Threads at end: " + Process.GetCurrentProcess().Threads.Count + ", time: " + stopWatch.Elapsed);

            testTask.Wait();
        }

        static void Main(string[] args)
        {
            ThreadPool.SetMaxThreads(MAX_REQS * 2, MAX_REQS * 2);
            ThreadPool.SetMinThreads(MAX_REQS, MAX_REQS);

            Console.WriteLine("Testing using GetStringAsync");
            RunTest(TestWithGetStringAsync);
            Console.ReadLine();

            Console.WriteLine("Testing using GetStringSync");
            RunTest(TestWithGetStringSync);
            Console.ReadLine();
        }
    }
}