首页
学习
活动
专区
圈层
工具
发布
首页
学习
活动
专区
圈层
工具
MCP广场
社区首页 >问答首页 >.net MAUI c#背景任务ContinueWith和通知事件

.net MAUI c#背景任务ContinueWith和通知事件
EN

Stack Overflow用户
提问于 2022-08-18 13:05:26
回答 2查看 1.1K关注 0票数 1

EDITSolved,见下面/编辑

这是个新手问题。

我只是在钻研c#和异步,为什么我想要:

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

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

下面是代码的一些部分:

AppViewModel:

代码语言:javascript
运行
复制
    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);
    }   

syncToolService:

代码语言:javascript
运行
复制
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会运行一个任务(它似乎也会这样做),并且在调试过程中逐行运行时,它似乎也会触发事件(似乎不会引发exeption),在使用这个堆栈的syncToolService.StartSyncPhaseAsync(e.phase + 1, this);之后,它会崩溃:

代码语言:javascript
运行
复制
>   [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() 

我也可能在我的设计中有一个普遍的问题,任何帮助都将是伟大的!

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

因此,这是一个基本的实际工作代码,它似乎没有锁定UI,也没有崩溃(崩溃是因为Microsoft.VisualBasic.FileIO.TextFieldParser试图读取一行,并找到了一个以引号开头的字段,并认为它将是一个封闭的引号,而它不是)

AppViewModel:

代码语言:javascript
运行
复制
    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);
    }   

syncToolService:

代码语言:javascript
运行
复制
   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);
//     [...]
   }

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

不管怎样,谢谢,这两个答案都有帮助,我会把这个问题标记为解决方案,那是更接近核心问题的(我想)

EN

回答 2

Stack Overflow用户

回答已采纳

发布于 2022-08-18 21:04:01

考虑到async/await,几乎没有必要使用task continuationsConfigureAwait

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

示例:

代码语言:javascript
运行
复制
// 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.
...

更新

作为对评论的回应,您可以使用异步/等待来启动一系列任务,而无需等待结果:

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

代码语言:javascript
运行
复制
// 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调用:

代码语言:javascript
运行
复制
// 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都不使用连续,而是使用await来排序任务:

代码语言:javascript
运行
复制
async void TopMethod()
{
    await ..Task1..;
    await ..Task2..;
    await ..Task3..;
}

这相当于Task1.ContinueWith(Task2.ContinueWith(Task3)); (在我的头上,我可能没有完全正确的语法)。

如果您处于后台线程(做了Task.Run),那么要执行UI调用,只需在Dispatcher.Dispatch( ... )中包装即可。如第一个代码片段所示。

票数 3
EN

Stack Overflow用户

发布于 2022-08-18 18:13:45

您可以在构造函数中捕获SynchronizationContext,也可以通过为捕获定义显式API来捕获syncToolService:

代码语言:javascript
运行
复制
public void CaptureSynchronizationContext(SynchronizationContext context)
{   
    var current = SynchronizationContext.Current;
    if (context is null)
    {            
        this.capturedScheduler = TaskScheduler.Current;
        return;
    } 
   SynchronizationContext.SetSynchronizationContext(context);
   this.capturedScheduler = TaskScheduler.FromCurrentSynchronizationContext();
   SynchronizationContext.SetSynchronizationContext(current);
}

为在指定的上下文中调用逻辑添加一些包装器:

代码语言:javascript
运行
复制
private void RunTaskWithContinuation(Task task, Action<Task> continuation)
{
  task.ConfigureAwait(false);
  task.ContinueWith(t => continuation(t), capturedScheduler);
  task.Start();
}

因此,在您的UI中的某个位置:

代码语言:javascript
运行
复制
// afaik you should call it once per every Window
syncToolService.CaptureSynchronizationContext(SynchronizationContext.Current);

上面的代码如下所示:

代码语言:javascript
运行
复制
// read local files
if (phase == 2)
{
    Task t = new Task(() => BPMSyncToolService.loadLocalData(viewModel.DataFiles));
    RunTaskWithContinuation(t, () => NotifySyncPhaseCompleted?.Invoke(this, new SyncPhaseCompletedEventArgs { phase = phase }));
}

没有经过测试,但我会先尝试一下这个想法。

顺便说一句,如果SynchronizationContext为null,则猜测您的问题将持续存在。

这里有重构的空间,只是想展示一下这个想法。

更新

在多线程环境中,有适合于报表的ReportProgress型工具。也许这就是你要找的。

但是它的工作方式和我上面做的一样--通过上下文捕获。

票数 1
EN
页面原文内容由Stack Overflow提供。腾讯云小微IT领域专用引擎提供翻译支持
原文链接:

https://stackoverflow.com/questions/73403608

复制
相关文章

相似问题

领券
问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档