C# 理解C中的异步和等待#

C# 理解C中的异步和等待#,c#,asynchronous,async-await,C#,Asynchronous,Async Await,我正在尝试学习异步,并在C#中等待。我有三个方法,当我尝试调用所有这三个方法时,我想知道异步调用在C#中是如何工作的 我应该在延迟5秒后同时获取所有三个方法,但是我的Delay2()方法在Delay1()方法的5秒后被调用,而Delay3()方法在Delay2()方法的5秒后被调用 有人能帮我理解C#中异步方法的工作原理吗 更新1 Async和Await假定是异步的,那么为什么我的常规语句会停止执行呢 public async Task Print() {

我正在尝试学习异步,并在C#中等待。我有三个方法,当我尝试调用所有这三个方法时,我想知道异步调用在C#中是如何工作的

我应该在延迟5秒后同时获取所有三个方法,但是我的
Delay2()
方法在
Delay1()
方法的5秒后被调用,而
Delay3()
方法在
Delay2()
方法的5秒后被调用

有人能帮我理解C#中异步方法的工作原理吗

更新1 Async和Await假定是异步的,那么为什么我的常规语句会停止执行呢

public async Task Print()
        {
            Console.WriteLine(await Delay1());

            Console.WriteLine("this is regular flow");
        }

异步不是并发的。每次调用wait都会阻止执行下面的代码,直到任务完成。在本例中,您可以通过将示例更改为如下所示来创建并发性

class Program
{
    static void Main()
    {
        new Content().Print().Wait();
        Console.Read();
    }
}

class Content
{

    public async Task<string> Delay1()
    {
        await Task.Delay(5000);
        return "hello";
    }
    public async Task<string> Delay2()
    {
        await Task.Delay(5000);
        return "hello";
    }
    public async Task<string> Delay3()
    {
        await Task.Delay(5000);
        return "hello";
    }

    public async Task Print()
    {
        var tasks = new[] {Delay1(), Delay2(), Delay3()};
        await Task.WhenAll(tasks);
        foreach(var result in tasks.Select(x => x.Result))
        {
            Console.WriteLine(result);
        }
    }
}
类程序
{
静态void Main()
{
新内容().Print().Wait();
Console.Read();
}
}
课堂内容
{
公共异步任务延迟1()
{
等待任务。延迟(5000);
回复“你好”;
}
公共异步任务延迟2()
{
等待任务。延迟(5000);
回复“你好”;
}
公共异步任务延迟3()
{
等待任务。延迟(5000);
回复“你好”;
}
公共异步任务打印()
{
var tasks=new[]{Delay1(),Delay2(),Delay3()};
等待任务。何时(任务);
foreach(任务中的var结果。选择(x=>x.result))
{
控制台写入线(结果);
}
}
}

您可以独立启动三个任务,并将它们存储在集合中。然后,您可以调用
wait Task.whalll
来阻止执行,直到所有这些任务都完成。之后,您可以循环使用结果,并根据需要使用它们。

异步/await语法允许编写比不使用更可读的模式。为了让你理解真正的意思,考虑你的片段改写如下:

class Content
{

    public void Delay1(Action<string> callback)
    {
        //something which takes 5000ms
        callback("hello");
    }
    public void Delay2(Action<string> callback)
    {
        //something which takes 5000ms
        callback("hello");
    }
    public void Delay3(Action<string> callback)
    {
        //something which takes 5000ms
        callback("hello");
    }
    public void Print(Action callback)
    {
        this.Delay1(x=> {
            Console.WriteLine(x);
            this.Delay2(y => {
                Console.WriteLine(y);
                this.Delay3(z=> {
                    Console.WriteLine(z);
                    callback();
                }); 
            });
        });
    }
}
课程内容
{
公共无效延迟1(操作回调)
{
//需要5000毫秒的东西
回拨(“你好”);
}
公共无效延迟2(操作回调)
{
//需要5000毫秒的东西
回拨(“你好”);
}
公共无效延迟3(操作回调)
{
//需要5000毫秒的东西
回拨(“你好”);
}
公共作废打印(操作回调)
{
这个.Delay1(x=>{
控制台写入线(x);
这个.Delay2(y=>{
控制台写入线(y);
这个.Delay3(z=>{
控制台写入线(z);
回调();
}); 
});
});
}
}
您不应该依赖于系统实际执行的操作(例如,在Task.Delay的情况下)。它可以将“延迟”中继到其他线程(线程池的队列),但它可能会以与调用方相同的方式执行作业

正如@Rob正确指出的,异步不是并发:代码是按顺序执行的


这只是为了让你明白:没什么了。

当你调用wait时,你要求curret方法停止执行,直到“waitied”调用结束;所以,当你说:

Console.WriteLine(await Delay1());
您要求当前方法(打印)停止,直到Delay1()完成

为了获得预期的结果,必须在不使用await指令的情况下调用它们(使用await调用异步方法不是强制性的)

下面是一个例子:

    public class Content
    {

        public async void Delay1()
        {
            await Task.Delay(5000);
            Console.WriteLine("hello");
        }
        public async void Delay2()
        {
            await Task.Delay(5000);
            Console.WriteLine("hello");
        }
        public async void Delay3()
        {
            await Task.Delay(5000);
            Console.WriteLine("hello");
        }
        public void Print()
        {
            Delay1();
            Delay2();
            Delay3();
        }
    }

编辑:正如@Adrian指出的,Print方法可能比三个Delay方法调用完成得更快。我在测试中没有注意到它,因为有一个控制台

引用更新

因为我的答案被否决的原因我不知道,因为否决的选民没有给出任何否决的理由,除了罗布,我在这里为我的答案辩护

这里的重点不是要证明任何人是错的,而是要确保OP收到正确的信息和事实

那么,问题是什么?我认为有两个问题:

  • 有人能帮我理解C#中异步方法的工作原理吗?(明确地问)
  • 为什么我的代码没有做我认为应该做的事情
  • 发布了许多答案来修复代码。纳尔逊的答案也有正确的代码,因此我从未投过反对票。我的问题不是这里提供的解决方案,而是“异步不是并发”的说法。我还想用简单的英语解释async await是如何工作的,因为OP明确要求这样做。这就是为什么我仍然坚持我的立场:

    Stephen Cleary是一位受人尊敬的作家,他在《并发性》一书中指出:

    异步编程

    一种并发形式,使用未来或回调来避免 不必要的线程

    未来(或承诺)是表示 一些将在将来完成的操作。现代未来 NET中的类型是Task和Task。较旧的异步API使用 回调或事件,而不是未来。异步编程是 围绕异步操作的思想:一些操作 这将在一段时间后完成。而 操作正在进行中,不会阻塞原始线程;这个 启动操作的线程可以自由执行其他工作。当 操作完成后,它通知其未来或调用其完成 回调事件,让应用程序知道操作已完成

    他接着说:

    异步编程是一种强大的并发形式,但直到最近,它还需要极其复杂的代码。VS2012中的异步和等待支持使异步编程几乎与同步(非当前)编程一样简单

    是一个stackoverflow答案,其中
    Console.WriteLine(await Delay1());
    
        public class Content
        {
    
            public async void Delay1()
            {
                await Task.Delay(5000);
                Console.WriteLine("hello");
            }
            public async void Delay2()
            {
                await Task.Delay(5000);
                Console.WriteLine("hello");
            }
            public async void Delay3()
            {
                await Task.Delay(5000);
                Console.WriteLine("hello");
            }
            public void Print()
            {
                Delay1();
                Delay2();
                Delay3();
            }
        }
    
    namespace TaskAsyncTests
    {
        using System.Threading.Tasks;
    
        class Program
        {
            static async Task<KeyValuePair<string, long>> TaskThis(Func<string> fn)
            {
                var watch = new System.Diagnostics.Stopwatch();
                watch.Start();
                var task = Task.Run(fn); //fn will be 1sec
                await Task.Delay(1000);  //also being delayed 1sec here
                var result = await task;
                watch.Stop();
                return new KeyValuePair<string, long>(result, watch.ElapsedMilliseconds); //result should only be approx. 1 sec though
            }
    
            static void Main(string[] args)
            {
                var watch = new System.Diagnostics.Stopwatch();
                watch.Start();
                var results = Run(Task.WhenAll(new[]
                {
                    TaskThis(LongProcessingFunction),
                    TaskThis(LongProcessingFunction),
                    TaskThis(LongProcessingFunction),
                }));
                watch.Stop();
                foreach (KeyValuePair<string, long> item in results)
                {
                    Console.WriteLine(@"result:= '{0}' ElapsedMilliseconds := {1}", item.Key, item.Value.ToString());
                }
                Console.WriteLine("total ElapsedMilliseconds := {0}", watch.ElapsedMilliseconds);
    
                watch.Reset();
                watch.Start();
                var result = Run(GetSomethingAsync());
                watch.Stop();
    
                Console.WriteLine(@"result->PropertyOne := '{0}' ElapsedMilliseconds := {1}", result.PropertyOne.Key, result.PropertyOne.Value.ToString());
                Console.WriteLine(@"result->PropertyTwo := '{0}' ElapsedMilliseconds := {1}", result.PropertyTwo.Key, result.PropertyTwo.Value.ToString());
                Console.WriteLine(@"result->PropertyThree := '{0}' ElapsedMilliseconds := {1}", result.PropertyThree.Key, result.PropertyThree.Value.ToString());
                Console.WriteLine("total ElapsedMilliseconds := {0}", watch.ElapsedMilliseconds);
    
                Console.ReadLine();
            }
    
            static string LongProcessingFunction()
            {
                Task.Delay(1000).Wait();
    
                return "Hello World";
            }
    
            static T Run<T>(Task<T> taskRunner)
            {
                return taskRunner.Result;
            }
    
            static T[] Run<T>(Task<T[]> taskRunner)
            {
                return taskRunner.Result;
            }
    
            static async Task<dynamic> GetSomethingAsync()
            {
                var resultsTask = Task.WhenAll(new[] 
                {
                    TaskThis(LongProcessingFunction),
                    TaskThis(LongProcessingFunction),
                    TaskThis(LongProcessingFunction)
                }).ConfigureAwait(false);
    
                // do other stuff here
                Task.Delay(2000).Wait();
    
                var results = await resultsTask;
                return new
                {
                    PropertyOne = results[0],
                    PropertyTwo = results[1],
                    PropertyThree = results[2]
                };
            }
        }
    }
    
    result:= 'Hello World' ElapsedMilliseconds := 1025
    result:= 'Hello World' ElapsedMilliseconds := 1014
    result:= 'Hello World' ElapsedMilliseconds := 1014
    total ElapsedMilliseconds := 1028
    result->PropertyOne := 'Hello World' ElapsedMilliseconds := 1001
    result->PropertyTwo := 'Hello World' ElapsedMilliseconds := 1001
    result->PropertyThree := 'Hello World' ElapsedMilliseconds := 1000
    total ElapsedMilliseconds := 2001