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

mec*_*ech 4 c# multithreading .net-core maui .net-6.0

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

这是一个新手问题。

我只是深入研究 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);
    }   
Run Code Online (Sandbox Code Playgroud)

同步工具服务:

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;
        }
    }
Run Code Online (Sandbox Code Playgroud)

基本上我认为 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() 
Run Code Online (Sandbox Code Playgroud)

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

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

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

应用程序视图模型:

>   [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() 
Run Code Online (Sandbox Code Playgroud)

同步工具服务:

    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);
    }   
Run Code Online (Sandbox Code Playgroud)

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

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

Too*_*eve 8

给定async/ await,几乎不需要使用task continuationsor ConfigureAwait

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

例子:

// 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.
...
Run Code Online (Sandbox Code Playgroud)

更新

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

如果您的顶级代码执行 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());
Run Code Online (Sandbox Code Playgroud)

如果您的顶级代码不执行 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());
Run Code Online (Sandbox Code Playgroud)

无论哪种情况,TopMethod 都不使用延续,而是使用awaits 对任务进行排序:

async void TopMethod()
{
    await ..Task1..;
    await ..Task2..;
    await ..Task3..;
}
Run Code Online (Sandbox Code Playgroud)

这相当于Task1.ContinueWith(Task2.ContinueWith(Task3)); (超出我的想象;我对此的语法可能不太正确。)


如果您位于后台线程 (did Task.Run),那么要执行UI 调用,只需将Dispatcher.Dispatch( ... ). 如第一个代码片段所示。