首页 > 解决方案 > 如何在 C# 中等待异步任务完成?

问题描述

我正在使用 uwp 应用程序中的 RenderTargetBitmap 将 UIelement 转换为流。但我不知道如何让线程等到流的转换。我尝试过使用这些建议,但找不到合适的解决方案。任何帮助深表感谢。

   {
      for (int count = 0; count <= Textpage.Children.Count; count++)
      {
         if (Textpage.Children[count] is Viewbox)
         {
            CustomView customView = (Textpage.Children[count] as Viewbox).Child as CustomView;
            UIElement frameworkElement = customView as UIElement;
            (Textpage.Children[count] as Viewbox).Child = null;
            var element = (PdfDocumentPanel.Children[count] as Border).Child as Canvas;
            Textpage.Children.Remove((Textpage.Children[count] as Viewbox));

            SaveCustomAnnotation(frameworkElement, pageIndex, documentToBeSaved);

         }
      }

   }

   Stream savedStream = new MemoryStream();
}

internal async void SaveCustomAnnotation(UIElement element, int pageIndex, PdfLoadedDocument loadedDocument)
{

   Type pageChild = null;

   IRandomAccessStream randomStream;

   randomStream = await Task.Run(() => ConvertToImage(element));

}

public async Task<IRandomAccessStream> ConvertToImage(UIElement element)
{

   InMemoryRandomAccessStream randomAccessStream = null;
   IBuffer pixelBuffer = null;
   RenderTargetBitmap bitmap = new RenderTargetBitmap();


   await bitmap.RenderAsync(element);

   pixelBuffer = await bitmap.GetPixelsAsync();

   var logicalDpi = DisplayInformation.GetForCurrentView().LogicalDpi;
   using (randomAccessStream = new InMemoryRandomAccessStream())
   {
      var encoder = await BitmapEncoder.CreateAsync(BitmapEncoder.PngEncoderId, randomAccessStream);
      encoder.SetPixelData(
      BitmapPixelFormat.Bgra8,
      BitmapAlphaMode.Ignore,
      (uint)bitmap.PixelWidth,
      (uint)bitmap.PixelHeight,
      logicalDpi,
      logicalDpi,
      pixelBuffer.ToArray());
      await encoder.FlushAsync();

   }
   return randomAccessStream;
}

我希望在移动初始化新内存流之前完全完成 SaveCustomannotation 方法

标签: c#async-await

解决方案


这里的问题是滥用async void这使您无法等待它。刚开始时的一个好规则是,如果您输入async void停止...从您的计算机退后一步,并告诉您自己可能有更好的方法...它们应该主要用于事件处理程序。

基本上SaveCustomAnnotation应该返回一个Task

internal async Task SaveCustomAnnotation(UIElement element, int pageIndex, PdfLoadedDocument loadedDocument)

然后你需要await

await SaveCustomAnnotation(frameworkElement, pageIndex, documentToBeSaved);

因此,它的调用方法也应该是async Task(一直到调用链),只有这样它们才应该是一个async void并且只有当它是一个事件处理程序时。

最后注

Async void方法具有不同的错误处理语义。async Task当一个异常从or方法中抛出时async Task<T>,该异常被捕获并放置在Task. 对于async void方法,没有 Task 对象(它们在未观察到的情况下运行),因此从async void方法抛出的任何异常都将直接在启动时处于活动状态的 SynchronizationContext 上引发。

简而言之,总是在此类方法中捕获和处理异常


正如乔纳森·蔡斯( Jonathon Chase )所逃避的(而且是正确的)。

如果这就是它所做的一切,您可能应该只从 main 方法调用它并等待它,因为它无论如何都会返回一个任务并标记为异步

internal async void SaveCustomAnnotation(UIElement element, int pageIndex, PdfLoadedDocument loadedDocument)
{
   Type pageChild = null;
   IRandomAccessStream randomStream;
   randomStream = await Task.Run(() => ConvertToImage(element));
}

您可以删除整个方法并替换

await SaveCustomAnnotation(frameworkElement, pageIndex, documentToBeSaved);

var readstream = await ConvertToImage(frameworkElement); 

这段代码还有很多不确定的地方,但是我认为它们可能应该属于另一个问题


推荐阅读