如何停止 Task.Run()?

Posted

技术标签:

【中文标题】如何停止 Task.Run()?【英文标题】:How can I stop the Task.Run()? 【发布时间】:2021-11-08 16:48:33 【问题描述】:

我对线程的概念比较陌生,我想在我的应用程序中使用Task,它是Thread 的一个组件,因为保存任务需要时间来执行。

这是我的代码:

    private void SaveItem(object sender, RoutedEventArgs e)
    
        // Button Save Click ( Save to the database )

        Task.Run(() =>
        
            var itemsS = Gridview.Items;

            Dispatcher.Invoke(() =>
            
                foreach (ItemsModel item in itemsS)
                
                    PleaseWaittxt.Visibility = Visibility.Visible;

                    bool testAdd = new Controller().AddItem(item);
                    if (testAdd)
                        Console.WriteLine("Add true to Items ");
                    else
                    
                        MessageBox.Show("Add failed");
                        return;
                    
                
                PleaseWaittxt.Visibility = Visibility.Hidden;
            );
        );

        MessageBox.Show("Save Done"); 

        // update the gridView
        var results = new Controller().GetAllItems();
        Gridview.ItemsSource = null;
        Gridview.ItemsSource = results;
        Gridview.Items.Refresh();
    

问题是当我保存所有项目时,我在数据库中得到了重复的数据。否则ItemsS的计数固定为300,但是保存后我得到了600,

Task.Run() 是否重复了保存任务到数据库?

注意:我正在开发 UI 项目(WPF 桌面应用)

【问题讨论】:

当内部 Action 无论如何都会被同步调用时,在该方法中调用 Task.Run() 有什么意义?最好删除该样板并按原样调用 Dispatcher。 任务执行需要时间(保存1000多行到数据库),因此我需要使用多线程。 @SimonC 他正在使用 UI 线程,为了不阻塞 UI,他使用 task.run 将工作推送到后台线程。 Dispatcher.Invoke 仍然在主线程上运行该方法,所以他正在尝试解决这个问题,我猜 @sommmen 是的,我也明白了。但在这种情况下,可以使用private async void SaveItem(whatever) await Task.Run() Task.Run(async () => ) @SimonC 你是对的 - 这实际上是正确的答案。问题是他也在做一些需要从任务中移出的 UI 工作 【参考方案1】:

您遇到的问题是因为您正在执行的任务不是并行运行,而是与应用程序的其余部分同步运行。

当您在 UI 应用程序的后台运行 CPU 密集型任务时,您需要使用实际线程或异步/等待 - 这是您尝试使用代码的方式。

你想要做的是类似这样的事情:

private async void SaveItem(object sender, RoutedEventArgs e) => await Task.Run(
/*optionally make this async too*/() => 
    // Execute your CPU-intensive task here
    Dispatcher.Invoke(() => 
        // Handle your UI updates here
    );
);

这只是一般概述,我不知道您的确切用例,但这应该可以让您朝着正确的方向开始。

在使用 Lambda 等时要厌倦一件事,那就是闭包。 如果您的应用程序倾向于使用大量内存,您可能需要重新考虑调用树的结构并尽量减少正在运行的应用程序中的闭包。

【讨论】:

我在数据库中仍然得到重复的结果!!【参考方案2】:

我想你需要一些类似的东西。 我很快就把它搞定了,但我希望它足以让你自己尝试修复。

private async void SaveItem(object sender, RoutedEventArgs e)

  try 
    var itemsS = GridviewServices.Items.ToList(); // to list makes shallow copy

    await Task.Run(() => 
          foreach (ItemsModel item in itemsS)
          
              bool testAdd = new Controller().AddItem(item);
           
      );

    // Dont update ui in task.run, because only the ui thread may access UI items
    // Do so here - after the await. (or use dispatcher.invoke).

   GridviewServices.Items.Clear(); 
   GridviewServices.Items = itemsS;
 
   catch  ...  // Handle exceptions, log them or something. Dont throw in async void!
 


我也认为这会起作用:

  private async void SaveItem(object sender, RoutedEventArgs e)
    
        // Button Save Click ( Save to the database )

        var itemsS = GridviewServices.Items;
        await Task.Run(() =>
        
                foreach (ItemsModel item in itemsS)
                
                    Dispatcher.Invoke(() => PleaseWaittxt.Visibility = Visibility.Visible;)

                    bool testAdd = new Controller().AddItem(item);
                    if (testAdd)
                        Console.WriteLine("Add true to Items ");
                    else
                    
                        MessageBox.Show("Add failed");
                        return;
                    
              
                
                Dispatcher.Invoke(() => PleaseWaittxt.Visibility = Visibility.Hidden;)
        );

        MessageBox.Show("Save Done"); 

        // update the gridView
        var results = new Controller().GetAllItems();
        Gridview.ItemsSource = null;
        Gridview.ItemsSource = results;
        Gridview.Items.Refresh();

    

【讨论】:

这个答案可能是一个答案,但它对我不起作用,我仍然得到重复的结果 也许-但这与您在这里提出的问题不同。为什么不在 Controller.AddItem 方法中首先检查实体是否存在于数据库中?也许将 theat 方法中的内容添加到 Q 中,以便我们查看。 谢谢,它现在可以工作了,这是 SQL Server 中的一个错误

以上是关于如何停止 Task.Run()?的主要内容,如果未能解决你的问题,请参考以下文章

使用异步方法等待 Task.Run 不会在正确的线程上引发异常

使用 Task.Run() 时如何限制最大线程数?

如何启动并等待 Task 运行

使用 Task.Run() 时如何避免 OutOfMemoryException? [复制]

Task.Run使用默认线程池

IIS 上任务的可靠后台队列