.net MAUI c# 后台任务ContinueWith和通知事件

2024-04-12

[编辑]已解决,见下文[/编辑]

这是一个新手问题。

我只是深入研究 C# 和异步,为什么我想要:

  • 单击按钮
  • 按顺序运行多个任务,但在后台线程中一个接一个地运行
  • 如果可能的话,正在运行的任务应该通知它们的进度

现在我可以单击按钮并启动任务链,但在完成事件中我希望(用于测试)每次任务完成时显示一个消息框。这可能会导致崩溃(?),我不知道为什么,因为我以为我会在 ui 线程内......

这是代码的一些部分:

应用程序视图模型:

    void handlePhaseCompletedEvent(object sender, SyncPhaseCompletedEventArgs e)
    {
        Shell.Current.DisplayAlert("TEST", "PHASE " + e.phase.ToString(), "OK"); // <<<< doesn't show up, maybe because its crashing a short time after?
        syncToolService.StartSyncPhaseAsync(e.phase + 1, this); // <<<< seems to crash here?
    }

    [RelayCommand]
    async Task StartSyncAsync()
    {
        syncToolService.NotifySyncPhaseCompleted += handlePhaseCompletedEvent;
        syncToolService.StartSyncPhaseAsync(0, this);
    }   

同步工具服务:

public event EventHandler<SyncPhaseCompletedEventArgs> NotifySyncPhaseCompleted;

    public async Task StartSyncPhaseAsync(int phase, AppViewModel viewModel)
    {
        // search for Remote-peer
        if (phase == 0)
        {
            Task t = new Task(() => Task.Delay(100)); // dummy, not implemented yet
            t.ConfigureAwait(false);
            t.ContinueWith(t => NotifySyncPhaseCompleted?.Invoke(this, new SyncPhaseCompletedEventArgs { phase = phase }));
            t.Start();
            return;
        }

        // Remote Sync start preparations
        if (phase == 1)
        {
            Task t = new Task(() => Task.Delay(100)); // dummy, not implemented yet
            t.ConfigureAwait(false);
            t.ContinueWith(t => NotifySyncPhaseCompleted?.Invoke(this, new SyncPhaseCompletedEventArgs { phase = phase }));
            t.Start();
            return;
        }

        //////// LOCAL PREPARATIONS

        // read local files
        if (phase == 2)
        {
            Task t = new Task(() => BPMSyncToolService.loadLocalData(viewModel.DataFiles));
            t.ConfigureAwait(false);
            t.ContinueWith(t => NotifySyncPhaseCompleted?.Invoke(this, new SyncPhaseCompletedEventArgs { phase = phase }));
            t.Start();
            return;
        }
    }

基本上我认为 StartSyncPhaseAsync 会运行一个任务(它似乎是这样做的) 它似乎也触发了事件(这似乎不会引发异常) 当在调试中逐行运行时,它会崩溃syncToolService.StartSyncPhaseAsync(e.phase + 1, this);有了这个堆栈:

>   [Exception] WinRT.Runtime.dll!WinRT.ExceptionHelpers.ThrowExceptionForHR.__Throw|20_0(int hr)   
    [Exception] Microsoft.WinUI.dll!Microsoft.UI.Xaml.Controls.ContentDialog._IContentDialogFactory.CreateInstance(object baseInterface, out System.IntPtr innerInterface)  
    [Exception] Microsoft.WinUI.dll!Microsoft.UI.Xaml.Controls.ContentDialog.ContentDialog()    
    [Exception] Microsoft.Maui.Controls.dll!Microsoft.Maui.Controls.Platform.AlertManager.AlertRequestHelper.OnAlertRequested(Microsoft.Maui.Controls.Page sender, Microsoft.Maui.Controls.Internals.AlertArguments arguments)  
    System.Private.CoreLib.dll!System.Runtime.ExceptionServices.ExceptionDispatchInfo.Throw()   
    System.Private.CoreLib.dll!System.Threading.Tasks.Task.ThrowAsync.AnonymousMethod__128_1(object state)  
    System.Private.CoreLib.dll!System.Threading.QueueUserWorkItemCallbackDefaultContext.Execute()   
    System.Private.CoreLib.dll!System.Threading.ThreadPoolWorkQueue.Dispatch()  
    System.Private.CoreLib.dll!System.Threading.PortableThreadPool.WorkerThread.WorkerThreadStart() 

我的设计中也可能存在一般问题,任何帮助都会很棒!

[UPDATE]它现在按预期运行。 新手想法:

  • 来自 ToolmakerSteve 的回答https://stackoverflow.com/a/73409415/4232410 https://stackoverflow.com/a/73409415/4232410我想“嘿,但这就是我首先尝试的,它锁定了用户界面”。然后我看了 and https://www.youtube.com/watch?v=ZTKGRJy5P2M https://www.youtube.com/watch?v=ZTKGRJy5P2M我看到“嘿,这基本上就是上面提到的,而且它有效,所以我的错在哪里(现在当我写这篇文章时,我看到了他的妻子,非常感谢!)
  • Ryan 提到了“ReportProgress”(这就是我偶然发现上述视频的方式),它起作用了,也谢谢你!

所以这基本上是实际的工作代码,似乎不会锁定 UI 并且不会崩溃(崩溃是因为 Microsoft.VisualBasic.FileIO.TextFieldParser 尝试读取一行并找到一个以引号开头的字段并认为它将是一个封闭的引用,但事实并非如此)

应用程序视图模型:

    private void HandleSyncProgressChanged(object sender, SyncStatus e)
    {
        NumFilesProcessed = e.filesProcessed;
        NumFilesNotFound = e.filesNotFound;
        AktueleAufgabe = e.workingPhase;
    }
    [RelayCommand]
    async Task StartSyncAsync()
    {
        Progress<SyncStatus> progress=new Progress<SyncStatus>();
        progress.ProgressChanged += HandleSyncProgressChanged;
        await BPMSyncToolService.StartSyncPhaseAsync(this, progress);
    }   

同步工具服务:

   public static async Task StartSyncPhaseAsync(AppViewModel viewModel, IProgress<SyncStatus> progress)
    {
        SyncStatus report = new SyncStatus();
        report.workingPhase = "Suche Synchronisationspartner";
        progress.Report(report);
        // search for Remote-peer
        await Task.Delay(100); // dummy, not implemented yet

        report.workingPhase = "Starte Vorbereitungen beim Synchronisationspartner";
        progress.Report(report);
        // Remote Sync start preparations
        await  Task.Delay(100); // dummy, not implemented yet

        //////// LOCAL PREPARATIONS

        report.workingPhase = "lese lokale Dateien";
        progress.Report(report);
        // read local files
        await BPMSyncToolService.LoadLocalDataAsync(viewModel.DataFiles, progress, report);
//     [...]
   }

我实际上看不到的是已处理文件的计数,也许它太快了,不知道,将在需要更多时间的进一步任务中看到

无论如何,谢谢,两个答案都有帮助,我将把其中一个标记为解决方案,这更接近核心问题(我认为)


Given async/await,几乎不需要使用task continuations or ConfigureAwait.

  • 要在后台启动序列,请将序列包装在Task.Run.
  • 要报告 UI 线程的进度,请使用Dispatcher.Dispatch.

Example:

// IMPORTANT: `await`.
// Otherwise, current method would continue before Task.Run completes.
await Task.Run(async () =>
{
    // Now on background thread.
    ...

    // Report progress to UI.
    Dispatcher.Dispatch(() =>
    {
        // Code here is queued to run on MainThread.
        // Assuming you don't need to wait for the result,
        // don't need await/async here.
    }

    // Still on background thread.
    ...
};

// This is effectively the "continuation": Code here runs after Task.Run completes.
...

UPDATE

作为对评论的回应,这就是使用 async/await 启动一系列任务的方式,without等待结果:

如果您的顶级代码执行 UI 调用:

// This queues an independent execution to MainThread.
// We don't "await" the Dispatch, because we want it to run independently.
Dispatcher.Dispatch(async () => await TopMethod());

如果您的顶级代码不执行 UI 调用:

// This queues an independent execution to the Thread Pool.
// We don't "await" the Run, because we want it to run independently.
Task.Run(async () => await TopMethod());

在任何一种情况下,TopMethod 都不使用延续,而是使用awaits 对任务进行排序:

async void TopMethod()
{
    await ..Task1..;
    await ..Task2..;
    await ..Task3..;
}

这相当于Task1.ContinueWith(Task2.ContinueWith(Task3));(在我的脑海中;我的语法可能不太正确。)


如果你在后台线程上(Task.Run),然后做UI calls,简单地包裹在Dispatcher.Dispatch( ... )。如第一个代码片段所示。

本文内容由网友自发贡献,版权归原作者所有,本站不承担相应法律责任。如您发现有涉嫌抄袭侵权的内容,请联系:hwhale#tublm.com(使用前将#替换为@)

.net MAUI c# 后台任务ContinueWith和通知事件 的相关文章

  • 赋值运算符和复制构造函数有什么区别?

    我不明白C 中赋值构造函数和复制构造函数之间的区别 是这样的 class A public A cout lt lt A A lt lt endl The copy constructor A a b The assignment cons
  • Mono 无法保存用户设置

    我在 Mono Ubuntu 上保存用户设置时遇到问题 这是代码示例 private void Form1 Load object sender EventArgs e string savedText Properties Setting
  • 在 C++ 中分割大文件

    我正在尝试编写一个程序 该程序接受一个大文件 任何类型 并将其分成许多较小的 块 我想我已经有了基本的想法 但由于某种原因我无法创建超过 12 kb 的块大小 我知道谷歌等上有一些解决方案 但我更感兴趣的是了解这个限制的根源是什么 然后实际
  • 处理 fanart.tv Web 服务响应 JSON 和 C#

    我正在尝试使用 fanart tv Webservice API 但有几个问题 我正在使用 Json Net Newtonsoft Json 并通过其他 Web 服务将 JSON 响应直接反序列化为 C 对象 这里的问题是元素名称正在更改
  • 有什么工具可以说明每种方法运行需要多长时间?

    我的程序的某些部分速度很慢 我想知道是否有我可以使用的工具 例如它可以告诉我可以运行 methodA 花了 100ms 等等 或者类似的有用信息 如果您使用的是 Visual Studio Team System 性能工具 中有一个内置分析
  • std::map 和二叉搜索树

    我读过 std map 是使用二叉搜索树数据结构实现的 BST 是一种顺序数据结构 类似于数组中的元素 它将元素存储在 BST 节点中并按其顺序维护元素 例如如果元素小于节点 则将其存储在节点的左侧 如果元素大于节点 则将其存储在节点的右侧
  • 为什么 BOOST_FOREACH 不完全等同于手工编码的?

    From 增强文档 http www boost org doc libs 1 48 0 doc html foreach html foreach introduction what is literal boost foreach li
  • VS30063:您无权访问 https://dev.azure.com

    我正在尝试在 asp net core 2 1 mvc 应用程序中使用以下代码连接 Azure DevOps Uri orgUrl new Uri https dev azure com xxxxx String personalAcces
  • 如何用 kevent() 替换 select() 以获得更高的性能?

    来自Kqueue 维基百科页面 http en wikipedia org wiki Kqueue Kqueue 在内核和用户空间之间提供高效的输入和输出事件管道 因此 可以修改事件过滤器以及接收待处理事件 同时每次主事件循环迭代仅使用对
  • 单元测试失败,异常代码为 c0000005

    我正在尝试使用本机单元测试项目在 Visual Studios 2012 中创建单元测试 这是我的测试 TEST METHOD CalculationsRoundTests int result Calculations Round 1 0
  • 为什么 FTPWebRequest 或 WebRequest 通常不接受 /../ 路径?

    我正在尝试从 ftp Web 服务器自动执行一些上传 下载任务 当我通过客户端甚至通过 Firefox 连接到服务器时 为了访问我的目录 我必须指定如下路径 ftp ftpserver com AB00000 incoming files
  • C# 搜索目录中包含字符串的所有文件,然后返回该字符串

    使用用户在文本框中输入的内容 我想搜索目录中的哪个文件包含该文本 然后我想解析出信息 但我似乎找不到该字符串或至少返回信息 任何帮助将不胜感激 我当前的代码 private void btnSearchSerial Click object
  • 过期时自动重新填充缓存

    我当前缓存方法调用的结果 缓存代码遵循标准模式 如果存在 则使用缓存中的项目 否则计算结果 在返回之前将其缓存以供将来调用 我想保护客户端代码免受缓存未命中的影响 例如 当项目过期时 我正在考虑生成一个线程来等待缓存对象的生命周期 然后运行
  • 无法使用 Ninject 将依赖项注入到从 Angular 服务调用的 ASP.NET Web API 控制器中

    我将 Ninject 与 ASP NET MVC 4 一起使用 我正在使用存储库 并希望进行构造函数注入以将存储库传递给其中一个控制器 这是实现 StatTracker 接口的上下文对象 EntityFramework public cla
  • 为什么我使用google'smtp'无法发送电子邮件?

    我有以下程序使用 smtp gmail com 587 发送电子邮件 namespace TestMailServer class Program static void Main string args MailMessage mail
  • 如何重新启动死线程? [复制]

    这个问题在这里已经有答案了 有哪些不同的可能性可以带来死线程回到可运行状态 如果您查看线程生命周期图像 就会发现一旦线程终止 您就无法返回到新位置 So 没有办法将死线程恢复到可运行状态 相反 您应该创建一个新的 Thread 实例
  • 我应该在应用程序退出之前运行 Dispose 吗?

    我应该在应用程序退出之前运行 Dispose 吗 例如 我创建了许多对象 其中一些对象具有事件订阅 var myObject new MyClass myObject OnEvent OnEventHandle 例如 在我的工作中 我应该使
  • 热重载时调用方法

    我正在使用 Visual Studio 2022 和 C 制作游戏 我想知道当您热重新加载应用程序 当它正在运行时 时是否可以触发一些代码 我基本上有 2 个名为 UnloadLevel 和 LoadLevel 的方法 我想在热重载时执行它
  • 在基类集合上调用派生方法

    我有一个名为 A 的抽象类 以及实现 A 的其他类 B C D E 我的派生类持有不同类型的值 我还有一个 A 对象的列表 abstract class A class B class A public int val get privat
  • 如何使用 std::array 模拟 C 数组初始化“int arr[] = { e1, e2, e3, ... }”行为?

    注意 这个问题是关于不必指定元素数量并且仍然允许直接初始化嵌套类型 这个问题 https stackoverflow com questions 6111565 now that we have stdarray what uses are

随机推荐