You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

如何在非async的Cmdlet重写方法中正确调用async异步方法?

问题描述

我编写了一个异步方法,需要在继承自.NET System.Management.Automation.Cmdlet 类的子类重写方法中调用它。

我平时调用该方法的方式如下:

public async Task TestReadingFile()
{
  using MyReader reader = new MyReader(SetupMyStream("Contoso.txt"));

  await foreach (MyDataObject obj in reader.GetFrames())
  {
    Debug.Print($"{reader.Progress}: {obj.GetType().Name} - {obj}");
  }
}

现在在我的Cmdlet子类重写的ProcessRecord方法中,无法使用async修饰,但我需要等待异步操作完成。我该怎么实现?

我原本尝试了以下代码:

protected override void ProcessRecord()
{
  using MyReader reader = new MyReader(MyFilePath);

  IAsyncEnumerator<MyDataObject> enumerator = reader.GetFrames().GetAsyncEnumerator();

  while (enumerator.MoveNextAsync().GetAwaiter().GetResult())
  {
    MyDataObject obj = enumerator.Current;

    Debug.Print($"{reader.Progress}: {obj.GetType().Name} - {obj}");
  }
}

但这段代码抛出了InvalidOperationException,请问正确的实现方式是什么?


解决方案

在Cmdlet的同步方法中处理异步枚举时,核心要解决异步操作的上下文死锁问题,同时正确管理异步枚举器的生命周期。以下是两种可靠的实现方式:

方式一:手动正确处理异步枚举器生命周期

手动调用异步枚举器的MoveNextAsync和DisposeAsync,并通过ConfigureAwait(false)避免上下文绑定导致的死锁:

protected override void ProcessRecord()
{
  using MyReader reader = new MyReader(MyFilePath);
  var enumerator = reader.GetFrames().GetAsyncEnumerator();
  try
  {
    while (enumerator.MoveNextAsync().ConfigureAwait(false).GetAwaiter().GetResult())
    {
      MyDataObject obj = enumerator.Current;
      Debug.Print($"{reader.Progress}: {obj.GetType().Name} - {obj}");
    }
  }
  finally
  {
    // 确保异步枚举器被正确释放
    enumerator.DisposeAsync().ConfigureAwait(false).GetAwaiter().GetResult();
  }
}

方式二:封装异步逻辑到线程池上下文

利用Task.Run将异步逻辑调度到线程池,脱离原始同步上下文后再阻塞等待,写法更简洁:

protected override void ProcessRecord()
{
  Task.Run(async () =>
  {
    using MyReader reader = new MyReader(MyFilePath);
    await foreach (MyDataObject obj in reader.GetFrames().ConfigureAwait(false))
    {
      Debug.Print($"{reader.Progress}: {obj.GetType().Name} - {obj}");
    }
  }).GetAwaiter().GetResult();
}

原代码报错原因

你的代码未正确处理异步枚举器的DisposeAsync操作,同时调用MoveNextAsync().GetAwaiter().GetResult()时,异步操作尝试回到被同步阻塞占用的原始上下文,触发了死锁,最终抛出InvalidOperationException。

内容的提问来源于stack exchange,提问作者AxD

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.07.26 14:52:17