多线程任务更新 1 个进度条 - UI C# WPF

     2023-02-16     167

关键词:

【中文标题】多线程任务更新 1 个进度条 - UI C# WPF【英文标题】:Multiple threads tasks updating 1 progressbar - UI C# WPF 【发布时间】:2021-07-16 19:30:51 【问题描述】:

我一直在四处寻找有类似问题的人,但没有找到任何东西。我正在使用 WPF UI 编写 C# 应用程序。

要更新进度条,我不能像以前(在 CLI 中)那样使用多线程,因为它告诉我如果 UI 元素不是来自主线程,我就无法更新它。

一种解决方案是创建后台工作人员。我已经实现了这个解决方案并且效果很好,但是我希望将任务分配给更多的工作线程/线程(多线程)以提高效率。

我不知道我必须采取的方向。如果有人能指导我解决这个问题,那将更受欢迎。

这是我的代码:(用于使用 MVVM 模式进行编码,只是在这里粘贴我的代码对您来说更简单)

public partial class testFunctionTaskPanel: Page

    private BackgroundWorker backgroundWorker1 = new BackgroundWorker();

    private string myURL;

    public testFunctionTaskPanel()
    
        InitializeComponent();
    

    private void startButton_Click(object sender, RoutedEventArgs e)
    
        myURL = myURL.Text;

        myResults.Items.Clear();
        myResults.Items.Add("----Starting----");

        backgroundWorker1.WorkerReportsProgress = true;
        backgroundWorker1.ProgressChanged += ProgressChanged;
        backgroundWorker1.DoWork += DoWork;
        backgroundWorker1.RunWorkerCompleted += BackgroundWorker_RunWorkerCompleted;
        backgroundWorker1.RunWorkerAsync();
    

    private void DoWork(object sender, DoWorkEventArgs e)
    
        int length = myLoadedList.Items.Count;


        for (int i = 1; i <= length; i++)
        
            try
            
                HttpRequest req = new HttpRequest();
                req.Proxy = null;
                req.ConnectTimeout = 5000;
                req.IgnoreProtocolErrors = true;

                string get = myURL + myLoadedList.Items[i].ToString();
                var response = req.Get(get);

                if (response.StatusCode == Leaf.xNet.HttpStatusCode.OK)
                
                    this.Dispatcher.Invoke(() =>
                    
                        myResults.Items.Add(myLoadedList.Items[i].ToString());
                    );
                
            
            catch
            backgroundWorker1.ReportProgress(i);
        
    

    private void BackgroundWorker_RunWorkerCompleted(object sender, RunWorkerCompletedEventArgs e)
    
        myResults.Items.Add("----Finish----");
    

    private void ProgressChanged(object sender, ProgressChangedEventArgs e)
    
        // This is called on the UI thread when ReportProgress method is called
        progressbar.Value = e.ProgressPercentage;
    

【问题讨论】:

如果您使用任务和调用,您可能不需要BackgroundWorker。请参阅docs.microsoft.com/en-us/dotnet/api/… Invoke 将阻止您在尝试从另一个线程更新进度条时看到的错误消息。 这个话题有很好的处理here。 BackgroundWorker 已弃用。在现代 Sharp 中,使用异步方法(async/await)和 Task 是正确的。 大部分问题都是你自己制造的。 WPF 中的常见做法是使用数据绑定而不是直接访问 UI 元素。使用绑定时,可以在任何线程中更改数据。 @RobertHarvey 非常感谢您的帮助。我想,我尝试了很多解决方案,但迷失了自己。果然,答案就在眼前。再次感谢您的帮助 【参考方案1】:

你可以使用Dispatcher.Invoke():

Dispatcher.Invoke(() =>

    // Your code here
);

当您在线程或任务中时,只需调用它并将 UI 更新代码粘贴到其中。

【讨论】:

非常感谢您的帮助。我确实是“盲人”。我想,我尝试了很多解决方案,但迷失了自己。果然,答案就在眼前。再次感谢您的帮助。 @VicoLays 确保您的调度程序不为空,因为使用这种方法您的测试将失败。 请注意,Dispatcher.Invoke同步地执行指定的委托”。 IE。工作线程将阻塞,直到 UI 更新。【参考方案2】:

您可以使用Parallel 类或PLINQ 库来并行处理项目,使用多个ThreadPool 线程。为了向 UI 报告进度,您可以使用 IProgress&lt;T&gt; 抽象,其中 T 可以是您选择的任何类型。例如,它可以是ValueTuple&lt;string, bool&gt;,以便传达已处理的项目以及操作的成功/失败。通过这种方式,您可以创建一个与应用程序无关的类库方法。您可以在完全不同的应用程序(例如控制台应用程序)中逐字复制粘贴此方法,并且无需任何修改即可完全一样地工作。下面是这种方法的一个示例,它使用 PLINQ 库来处理并行性:

public static string[] ProcessAllItems(string[] items, string baseUrl,
    IProgress<(string, bool)> progress)

    return items
        .AsParallel()
        .AsOrdered()
        .WithDegreeOfParallelism(4)
        .Select(item =>
        
            HttpRequest req = new HttpRequest();
            req.Proxy = null;
            req.ConnectTimeout = 5000;
            req.IgnoreProtocolErrors = true;
            var response = req.Get(baseUrl + url);
            if (response.StatusCode == Leaf.xNet.HttpStatusCode.OK)
            
                progress.Report((item, true)); // Success
                return item;
            
            progress.Report((item, false)); // Failure
            return null;
        )
        .Where(result => result != null)
        .ToArray();

那么您所要做的就是在 UI 线程上创建一个Progress&lt;(string, bool)&gt; 对象,并传递一个处理来自后台线程的报告消息的委托。此委托应更新 myResultsprogressbar UI 元素。调用 ProcessAllItems 应包含在 await Task.Run 中,以保持 UI 响应。

private async void startButton_Click(object sender, RoutedEventArgs e)

    string baseUrl = myURL.Text;
    string[] items = myLoadedList.Items.Select(x => x.ToString()).ToArray();
    var completedCount = 0;

    var progress = new Progress<(string, bool)>(message =>
    
        if (message.Item2)
        
            myResults.Items.Add(message.Item1);
        
        completedCount++;
        progressbar.Value = completedCount * 100 / items.Length;
    );

    progressbar.Value = 0;
    myResults.Items.Clear();
    myResults.Items.Add("----Starting----");

    string[] results = await Task.Run(() =>
    
        return ProcessAllItems(items, baseUrl, progress);
    );

    progressbar.Value = 100;
    myResults.Items.Add("----Finish----");

注意startButton_Click 处理程序中的async 关键字,它可以使用await 运算符。

这个建议的要点是避免使用尴尬的Dispatcher.Invoke 方法,该方法鼓励将处理逻辑与表示逻辑以及technologically obsolete BackgroundWorker 类混合在一起。

【讨论】:

非常感谢您的出色帮助。你的解决方案真的很棒。我会尽快申请。【参考方案3】:

我的首选方法是:

    每个任务/线程更新一个单独的进度值 在主线程上运行的计时器平均每 X 毫秒的所有进度值,并更新进度条绑定到的属性。

这种方法的一个优点是可以在紧密的循环中更新值,而不会冒着调用请求淹没消息队列的风险。

【讨论】:

如果你使用了错误的计时器并且不释放事件处理程序,这也会带来内存泄漏的风险。 @XAMIMAX 好吧,是的,您需要正确使用这些工具。我认为这不需要提及。

如何在android开发中用asynctask异步更新ui界面

...本次的异步处理的一种方式AsyncTask,其实它的本质是一个线程池,所有提交的异步任务都会在这个线程池中的工作线程内执行,当工作线程需要跟UI线程交互时,工作线程会通过向在UI线程创建的Handler传递消息的方式,调用相关... 查看详情

recyclerview中多任务下载文件进度条更新的问题

在recyclerview或listview中进行下载时,由于条目复用等原因会导致下载的进度条更新错乱。你可能觉得条目复用问题我解决过那么多次,加个tag了啥的就解决了不是。有这个想法说明你没做过下载的处理。因为在下载的过程中,进... 查看详情

winform实现多线程异步更新ui(进度及状态信息)

...的用户体验。所以在大量数据操作的应用上,需要使用多线程来处理这种情况。在c#中使用多线程很方便只需要使用System.Threading.Thread的一个实例的Start方法就行了,但是如何实现多线程之间的交互就不是那么简单 查看详情

使用任务或线程从大数据表(进度数据库)逐行更新数据网格,但在更新期间保持 UI 响应

】使用任务或线程从大数据表(进度数据库)逐行更新数据网格,但在更新期间保持UI响应【英文标题】:Updatedatagridrowbyrowfromabigdatatable(Progressdatabase),usingataskorthread,butkeepingtheUIresponsiveduringtheupdate【发布时间】:2021-04-2706:29:27【... 查看详情

[c#]多线程总结(结合进度条)(代码片段)

线程生命周期未启动状态:当线程实例被创建但Start方法未被调用时的状况。就绪状态:当线程准备好运行并等待CPU周期时的状况。不可运行状态:已经调用Sleep方法已经调用Wait方法通过I/O操作阻塞死亡状态:当线程已完成执行... 查看详情

带有进度条更新的 C++ 多线程套接字操作

】带有进度条更新的C++多线程套接字操作【英文标题】:C++Multithreadedsocketoperationwithprogressbarupdate【发布时间】:2014-06-1221:34:20【问题描述】:我有一个正在实现的MFCDLL。主线程创建一个进度条(CProgressCtrl),然后启动一个CWinThr... 查看详情

c#上位机开发——多线程+进度条的使用(代码片段)

1.界面设计2.使用多线程修改进度条引入多线程类库命名空间:usingSystem.Threading;将变量添加到Form1类:privateThreadth;将方法添加到Form1类:privatevoidThreadTask()intstp;intnewval;Randomrnd=newRandom();while(true)stp=this.progressBar1.Step... 查看详情

c#上位机开发——多线程+进度条的使用(代码片段)

1.界面设计2.使用多线程修改进度条引入多线程类库命名空间:usingSystem.Threading;将变量添加到Form1类:privateThreadth;将方法添加到Form1类:privatevoidThreadTask()intstp;intnewval;Randomrnd=newRandom();while(true)stp=this.progressBar1.Step... 查看详情

如何在 UI 线程中工作时更新进度条

】如何在UI线程中工作时更新进度条【英文标题】:HowtoupdateprogressbarwhileworkingintheUIthread【发布时间】:2015-09-0311:50:30【问题描述】:我有一个ProgressBar和一个TreeView。我用一堆数据填充了TreeView,一旦应用它,我运行TreeView的visual... 查看详情

c#通过线程来控制进度条(转)--讲解多线程对界面的操作

//通过创建委托解决传递参数问题privatevoid_btnRun_Click(objectsender,System.EventArgse){RunTaskDelegaterunTask=newRunTaskDelegate(RunTask);//委托同步调用方式runTask(Convert.ToInt16(_txtSecond.Value));}//通过创建委托解决传递参数问题,通过委托的 查看详情

java多线程的应用场景

多线程的目的是充分利用cpu的运算性能场景1:web服务器采用多线程,以应对大量的请求。场景2:定时任务,如定期更新配置文件、定期采集信息。场景3:比较耗时的任务,如用户注册后的积分赠送。场景4:比较耗时的任务,... 查看详情

wpf多线程进度条更新进度(代码片段)

主要的使用类:DispatcherusingSystem;usingSystem.Collections.Generic;usingSystem.Linq;usingSystem.Text;usingSystem.Threading.Tasks;usingSystem.Windows;usingSystem.Windows.Controls;usingSystem.Windows.Data;usingSystem.Windows.Documents;usingSystem.Windows.Input;usingSystem.Windows.Media;us... 查看详情

pyqt使用线程更新进度条

】pyqt使用线程更新进度条【英文标题】:pyqtupdatingprogressbarusingthread【发布时间】:2019-12-1217:32:25【问题描述】:我想使用线程方法从我的main.py更新进度条。if__name__=="__main__":app=QApplication(sys.argv)uiplot=gui.Ui_MainWindow()Update_Progressbar... 查看详情

pyqt使用线程更新进度条

】pyqt使用线程更新进度条【英文标题】:pyqtupdatingprogressbarusingthread【发布时间】:2019-12-1217:32:25【问题描述】:我想使用线程方法从我的main.py更新进度条。if__name__=="__main__":app=QApplication(sys.argv)uiplot=gui.Ui_MainWindow()Update_Progressbar... 查看详情

c#大数据量问题

...随便怎么点界面,界面是不会死的,是如何实现的?我知道用线程,但是我试过,使用了一个现成来处理数据.还是一样界面死掉.我目前的思路:按钮事件启动一个无限循环的线程.楼主可能对多线程还不是很熟悉。首先要处理大量数据必... 查看详情

学习swoole需要掌握哪些基础知识

...要1分钟,总共就需要1万分钟,有点恐怖。这时可以用多线程,将1万条URL分成50等份,开50个线程,没个线程只需验证200条,这样所有的线程执行完是远小于1万分钟的。场景二:需要知道一个任务的执行进度,比如我们常看到的进... 查看详情

c#winform多线程

...开新的子窗体,请问这种设计的winform程序需不需要用多线程,为什么?最好给个例子,不要复制粘贴!!!分数另付什么样的程序需要用到多线程一般在一些网络软件方面需要多线程,单单是管理软件什么的不需要。在网络软... 查看详情

C#任务栏中的Windows 7进度条?

】C#任务栏中的Windows7进度条?【英文标题】:Windows7progressbarintaskbarinC#?【发布时间】:2010-11-2017:17:27【问题描述】:如果您在Windows7测试版中注意到,如果您复制文件或其他系统操作,任务栏中的Windows资源管理器图标将填满一... 查看详情