【问题标题】:Extending C# async/await await doesn't await扩展 C# async/await await 不等待
【发布时间】:2019-10-19 04:40:16
【问题描述】:

扩展:C# async/await await doesn't await

如果顺序执行的方法也存储在列表中,我如何将 ExecuteParallelAsync 添加到该列表中?

private async Task ExecuteSequential()
{
    List<Action> sequentialMethods = new List<Action>()
    {
        SomeMethod1,
        SomeMethod2,
        await ExecuteParallelAsync, // ???
        SomeMethod3,
        SomeMethod4
    };

    for ( int i = 0 ; i < sequentialMethods.Count ; i++ )
    {
        sequentialMethods.ElementAt( i ).Invoke();
    }
}

澄清:

private async Task ExecuteParallelAsync()
{
    List<Action> methods = new List<Action>()
    {
        MyMethod1,
        MyMethod2,
        MyMethod3
    };


    await Task.Run( () => { Parallel.ForEach( methods , ( currentMethod ) => currentMethod.Invoke() ); } );            
}

sequentialMethodsListAction>ExecuteParallelAsync 不是 Action。我已经尝试拆分列表,就像建议的那样。不幸的是,它没有帮助。

Marc 从原始代码中建议的方式可以正常工作。但是我想在每次(顺序)方法调用之后做一些额外的事情,这就是我尝试使用列表和循环而不是普通方法调用的原因。

但是当我这样做时,我又遇到了原来的问题,即 SomeMethod3 在 ExecuteParallelAsync 完成之前执行。

再次重申,ExecuteParallelAsync 中的所有内容都可以并且应该同时执行。 ExecuteSequential 中的所有内容都必须按顺序执行。

解决方案:

加布里埃尔是绝对正确的。关键的声明是这个

await Task.Run( () => { Parallel.ForEach( methods , ( currentMethod ) => currentMethod.Invoke() ); } );

当我删除每个异步和每个任务时,以便按顺序执行所有内容,这一行是关键:

Parallel.ForEach( methods , ( currentMethod ) => currentMethod.Invoke() );

当我使用最后一条语句时,一切正常。

感谢大家,您的所有想法、想法和努力都得到了帮助和赞赏。

【问题讨论】:

  • Await 是将方法切割成状态机的语法糖。它如何在多种方法中删除列表? await 应该在调用方法时完成,不能动态生成。这是在编译时完成的。
  • 根据我的经验,ParallelFor 不能很好地处理异步函数。
  • 这也取决于MyMethod1/MyMethod2/MyMethod3是如何实现的。它们是async 方法吗?
  • 不,他们不是。它们在基类中是普通的 protected virtual void,因此在派生类中是 protected override void。我更改的唯一两种方法是从 (async) void 到 async Task 的 ExecuteSequential 和 ExecuteParallelAsync。
  • 如果它们不是异步的,你不能等待它们。您创建一个任务,等待它,然后创建其他任务来运行非异步代码。为什么?这只是使您的代码效率降低,没有任何好处。只需对 使用平行线(基本上就是 Gabirel has said

标签: c# async-await


【解决方案1】:

我又遇到了原来的问题,SomeMethod3 在 ExecuteParallelAsync 完成之前执行

那么这不是异步编程的用例。您的要求是这是同步的。

尤其如此,因为您说MyMethod1/MyMethod2/MyMethod3 不是异步方法。如果是,那将是totally different thing。但由于它们不是,我认为在这里尝试使用 asyncawait 没有任何价值。

但不要将异步与并行混淆。看来您希望ExecuteParallelAsync 中调用的方法并行运行,这很好。你只是不需要asyncawait

例如:

private void ExecuteSequential()
{
    List<Action> sequentialMethods = new List<Action>()
    {
        SomeMethod1,
        SomeMethod2,
        ExecuteParallel,
        SomeMethod3,
        SomeMethod4
    };

    for ( int i = 0 ; i < sequentialMethods.Count ; i++ )
    {
        sequentialMethods.ElementAt( i ).Invoke();
    }
}

private void ExecuteParallel()
{
    List<Action> methods = new List<Action>()
    {
        MyMethod1,
        MyMethod2,
        MyMethod3
    };

    Parallel.ForEach( methods , ( currentMethod ) => currentMethod.Invoke() );            
}

【讨论】:

  • 非常感谢加布里埃尔。我发现了我的错误。我将编辑原始帖子并发布解决方案。
【解决方案2】:

您可以创建一个Action,当被调用时将启动任务并等待它完成,如下所示:

List<Action> sequentialMethods = new List<Action>()
{
    SomeMethod1,
    SomeMethod2,
    () => ExecuteParallelAsync().Wait(),
    SomeMethod3,
    SomeMethod4
};

【讨论】:

  • 感谢西奥多,但没有任何改变。我的方法完全错误吗?
  • 方法按以下顺序调用:SomeMethod1,然后 SomeMethod2,然后(并行:SomeMethod1 和 SomeMethod2 和 SomeMethod3),然后是 SomeMethod3,最后是 SomeMethod4。这不是你想要的吗?如果您不希望 SomeMethod1、SomeMethod2 和 SomeMethod3 被调用两次,只需将它们从列表中删除即可。
  • 这正是我想要的。但是,在实际应用程序中,方法按以下顺序调用:SomeMethod1、SomeMethod2、ExecuteParallelAsync 中的第一个方法、SomeMethod3、SomeMethod 4、ExecuteParallelAsync 中的 SecondMethod、应用程序中的某些方法、ExecuteParralelAsync 中的第三个方法,依此类推。所以关键是,ExecuteParallelAsync 必须在 SomeMethod3 执行之前完成,因为如果没有,应用程序稍后会崩溃。是否一切都按顺序执行,应用程序变慢了。
  • 我强烈怀疑MyMethod1MyMethod2MyMethod3async void 方法。
  • 不,他们不是。它们在基类中是普通的protected virtual void,因此在派生类中是protected override void。我更改的唯一两种方法是 ExecuteSequential 和 ExecuteParallelAsync 从 voidasync Task
【解决方案3】:

这是一个版本:

private async Task  ExecuteSequential()
{
    var sequentialMethods = new List<Func<Task>>()
    {
        () => Task.Run(SomeMethod1),
        () => Task.Run(() => SomeMethod2("Hey!")), 
        ExecuteParallelAsync, 
        () => Task.Run(SomeMethod3),
        () => Task.Run(SomeMethod4)
    };

    for ( int i = 0 ; i < sequentialMethods.Count ; i++ )
    {
        Task t = sequentialMethods[i].Invoke(); 
        await t;
        // or just await sequentialMethods[i]();
    }
}

为了记录,ExecuteSequential 可能只是一个标准的异步方法(在顺序执行方面非常好)。

private async Task ExecuteSequential()
{
  SomeMethod1();
  SomeMethod2();
  await ExecuteParallelAsync(); 
  SomeMethod3();
  SomeMethod4();
};

编辑:测试

代码

class Program
{
    public void MyMethod1() => Console.WriteLine("||My1");
    public void MyMethod2() => Console.WriteLine("||My2");
    public void MyMethod3() => Console.WriteLine("||My3");

    private async Task ExecuteParallelAsync()
    {
        Console.WriteLine("||Start");
        List<Action> methods = new List<Action>() { MyMethod1, MyMethod2, MyMethod3 };

        await Task.Run(() => { Parallel.ForEach(methods, 
           (currentMethod) => currentMethod.Invoke()); }); // This could be just 'currentMethod();' (no Invoke())
        Console.WriteLine("||End");
    }


    public void SomeMethod1() => Console.WriteLine("Some1");
    public void SomeMethod2(string s) => Console.WriteLine($"Some2: {s}");
    public void SomeMethod3() => Console.WriteLine("Some3");
    public void SomeMethod4() => Console.WriteLine("Some4");

    private async Task ExecuteSequential()
    {
        var sequentialMethods = new List<Func<Task>>()
        {
           () => Task.Run(SomeMethod1),
           () => Task.Run(() => SomeMethod2("Hey!")),
           ExecuteParallelAsync,
           () => Task.Run(SomeMethod3),
           () => Task.Run(SomeMethod4)
        };

        for (int i = 0; i < sequentialMethods.Count; i++)
        {
            await sequentialMethods[i]();
        }
    }

    static async Task Main(string[] args)
    {
        await new Program().ExecuteSequential();
        Console.WriteLine("All done");
    }
}

输出

Some1
Some2: Hey!
||Start
||My3
||My2
||My1
||End
Some3
Some4
All done

My1My2My3 将在执行之间更改顺序,但始终在 ||Start||End 内。

【讨论】:

  • 非常感谢您采用这种新方法。不幸的是,它并没有改变任何东西。 (参考你编辑前的回答)
  • “不幸的是,它并没有改变任何东西”是什么意思?这两种方法都会导致顺序执行。
  • 我添加了一个测试。
  • 非常感谢@tymtam 的所有努力,我真的很感激。我已经复制了您的测试设置,它绝对正确。然而,在我的实际应用程序中,它不起作用。所以我想,同时执行不是问题。但它会是什么?
猜你喜欢
  • 1970-01-01
  • 2021-12-11
  • 2016-03-08
  • 2020-08-03
  • 2021-01-26
  • 1970-01-01
  • 1970-01-01
  • 2019-04-14
  • 2018-09-23
相关资源
最近更新 更多