多个并行未定义的BackgroundWorker [重复]

Posted

技术标签:

【中文标题】多个并行未定义的BackgroundWorker [重复]【英文标题】:Multiple parallel undefined BackgroundWorker [duplicate] 【发布时间】:2021-09-23 07:57:39 【问题描述】:

我使用BackgroundWorker 复制Zip archive(获取进度的3td 部件库)中的文件,因此我不会阻塞UI,我可以升级ProgressBar 或取消复制。

这是BackgroundWorker的代码,source、target和name是通用变量:

private void _backgroundWorkerB1_DoWork(object sender, DoWorkEventArgs e)

    try
    
        //Check if the source directory exist
        if (Directory.Exists(source) == false)
        
            e.Result = "NotExist";
            return;
        

        //If the target not exist I will create it
        if (Directory.Exists(target) == false)
        
            Directory.CreateDirectory(target);
        

        string filename = target + "\\" +
            DateTime.Now.ToString("yyyy'-'MM'-'dd'_'HH'_'mm'_'ss") +
            "-" + name + ".zip";

        // 3td part library
        ArchivioB1.FileName = filename;

        ArchivioB1.OpenArchive(System.IO.FileMode.Create);

        ArchivioB1.OnOverallProgress += new BaseArchiver
            .OnOverallProgressDelegate(ArchivioB1_OnOverallProgress);

        // Source directory
        ArchivioB1.BaseDir = source;

        // Copy all files
        ArchivioB1.AddFiles("*.*");

        // Close
        ArchivioB1.CloseArchive();
    
    catch (Exception ex)
    
        e.Result = "Error";
        return;
    

我需要修改代码以使其与DataGrid 并行。我将在第一列有一个DataGrid,在第一列是文件名,在第二列是进度条,在第三列是状态,在第四列是文件的目的地。 DataGrid 可以有未定义的行,这些行可以超过 CPU 的核心。如何并行运行相同的函数,如果行数超过 CPU 内核,系统必须等待其中一个空闲并继续下一个副本?

我不明白为什么社区必须在没有像他们想象的那样重复时关闭问题,无论如何,如果有人需要与进度并行复制存档中的文件 此后使用工作代码进行最终编辑:

private async void btBackup_Click(object sender, RoutedEventArgs e)
    
        btBackup.IsEnabled = false; 
        btCancel.IsEnabled = true; 

        var row_list1 = GetDataGridRows(dgwStation);
        List<Task> tasks = new List<Task>();
        ZipForge[] ZipList = new ZipForge[DataGridItemsList.Count];

        tokenSource = new CancellationTokenSource();

        foreach (DataGridRow single_row in row_list1)
        
            int riga = single_row.GetIndex();

            ZipList[riga] = new ZipForge();
            tasks.Add(Task.Run(() => CopyTest(riga,ZipList[riga])));
        

        await Task.WhenAll(tasks);

        if (generalerror)
            tbkStato.Text = "Completed with error";
        else if (tbkStato.Text != "Cancelled")
            tbkStato.Text = "Completed";
    
private void btCancel_Click(object sender, RoutedEventArgs e)
    
        try
        
            if (tokenSource != null) //se il token non è nullo, posso chiedere la cancellazione
            
                //tbkStato.Text = "Cancelled";

                tokenSource.Cancel();
            
        
        catch (Exception ex)
        
            MessageBox.Show(ex.Message, "Error", MessageBoxButton.OK, MessageBoxImage.Error);
        
    
public void CopyTest(int rowindex, ZipForge ArchivioB1)
    
        string nome1 = "";
        try
        
            //Takes data from DatGridItemsList
            string source = DataGridItemsList[rowindex].Source, target = DataGridItemsList[rowindex].Target, filename = DataGridItemsList[rowindex].FileName;

            tbkStato.Dispatcher.Invoke(new Action(() =>  tbkStato.Text = "Check source"; ));

            if (Directory.Exists(source) == false)
            
                DataGridItemsList[rowindex].State = "Not Exist";
                dgwStation.Dispatcher.Invoke(new Action(() =>  dgwStation.Items.Refresh(); ));
                generalerror = true;
                return;
            

            tbkStato.Dispatcher.Invoke(new Action(() =>  tbkStato.Text = "Check target"; ));

            if (Directory.Exists(target) == false)
            
                Directory.CreateDirectory(target);
            

            DataGridItemsList[rowindex].State = "creating Zip";
            dgwStation.Dispatcher.Invoke(new Action(() =>  dgwStation.Items.Refresh(); ));

            nome1 = target + "\\" + DateTime.Now.ToString("yyyy'-'MM'-'dd'_'HH'_'mm'_'ss") + "-" + filename + ".zip";

            ArchivioB1.FileName = nome1;

            ArchivioB1.OpenArchive(System.IO.FileMode.Create,FileAccess.Write);

            ArchivioB1.Comment = rowindex.ToString();

            ArchivioB1.OnOverallProgress += new BaseArchiver.OnOverallProgressDelegate(ArchivioB1_OnOverallProgress);

            ArchivioB1.BaseDir = source;

            // Copia tutti i file nell'archivio creato
            ArchivioB1.AddFiles("*.*");

            if (tokenSource.Token.IsCancellationRequested) //Interruzzione dell'utente
            
                tokenSource.Token.ThrowIfCancellationRequested();
            
            else
            

                ArchivioB1.CloseArchive();

                DataGridItemsList[rowindex].State = "Completed";
                dgwStation.Dispatcher.Invoke(new Action(() =>  dgwStation.Items.Refresh(); ));
            
        

        catch (OperationCanceledException)
        
            ArchivioB1.CloseArchive();

            if (File.Exists(nome1))
                File.Delete(nome1);

            DataGridItemsList[rowindex].State = "Cancelled";
            dgwStation.Dispatcher.Invoke(new Action(() =>  dgwStation.Items.Refresh(); ));
            tbkStato.Dispatcher.Invoke(new Action(() =>  tbkStato.Text = "Cancelled"; ));
            return;
        
        catch (Exception ex)
        
            ArchivioB1.CloseArchive();

            if (File.Exists(nome1))
                File.Delete(nome1);

            DataGridItemsList[rowindex].State = ex.Message.ToString();
            dgwStation.Dispatcher.Invoke(new Action(() =>  dgwStation.Items.Refresh(); ));
            generalerror = true;
            return;
        

    
private void ArchivioB1_OnOverallProgress(object sender, double progress, TimeSpan timeElapsed, TimeSpan timeLeft, ProcessOperation operation, ProgressPhase progressPhase, ref bool cancel)
    
        if (tokenSource.Token.IsCancellationRequested) //Interruzzione dell'utente
        
            cancel = true;
            //tokenSource.Token.ThrowIfCancellationRequested();
        

        ZipForge Archivio = (ZipForge)sender;

        int indice = Convert.ToInt32(Archivio.Comment);

        DataGridItemsList[indice].Progress = Convert.ToInt32(progress);
        dgwStation.Dispatcher.Invoke(new Action(() =>  dgwStation.Items.Refresh(); ));
    

【问题讨论】:

你也可以去how can I limit parallel-foreach看看 @Clemens 这个问题与that 问题有什么关系?这个问题与异步操作无关,而且这些操作也不太可能完全受 I/O 限制。归档文件也可能非常受 CPU 限制。 Paolo 您能否在问题中包含ArchivioB1_OnOverallProgress 事件处理程序? 所以也可能有一个_backgroundWorkerB1_ProgressChanged 事件处理程序。你能把它包括在问题中吗?我们必须查看您当前更新DataGrid 的方式和位置,否则我们如何提供帮助? @TheodorZoulias 在BackgroundWorker 中,我只是更新了一般的ProgressBarTextBlock。我需要修改代码以并行运行以升级DataGrid 【参考方案1】:

我建议使用 Tasks 而不是 Backgroundworkers。您可以使用 Task.WhenAll... 以简单的方式运行任意数量的 Tasks...

如果您想同步执行任务,只需省略 await...

public async ExecuteMultipleTasksSimultaneously()


List<Task> tasks = new List<Task>()

    Task.Factory.StartNew(() => ActionA()),
    Task.Factory.StartNew(() => ActionB()),
    Task.Factory.StartNew(() => ActionC())
;

//Execute all tasks "at the same time"...

await Task.WhenAll(tasks); 
 // or await Task.WhenAny(tasks);  
// or await Task.WaitAll(tasks) 
//or Task.WaitAny(tasks)  depending on your execution needs 

// Continue on this thread... 

//Alternative to Task.Factory.StartNew(() => ActionA()) you could use 
// Task.Run(()=> ActionA())


我希望这指明了正确的方向。最好的问候。

【讨论】:

我需要多次启动同一个Action,我只是改变源和目标,因为代码会做同样的事情。我编辑了我的问题,您可以在其中看到我用来测试的代码,但最终结果不是我所表达的 只需为动作提供不同的参数。您也可以在不等待结果的情况下启动任务。只需调用 Task.Run(()=> SameAction(differentparameters)。要从后台操作更新 UI,请使用 App.Current.Dispatcher.Invoke(() => UpdateUIFunction()); 太多操作更新 UI 太频繁会影响(减慢)你的 UI。我建议你定期更新 UI 大约 200 毫秒或更长时间。但是,对于这类事情,我真的建议实现 MVVM 模式(绑定)。直接更新 UIcomponent 不是一个好方法去做这类事情。

以上是关于多个并行未定义的BackgroundWorker [重复]的主要内容,如果未能解决你的问题,请参考以下文章

使用队列在两个 BackgroundWorker 之间传递数据

C# backgroundworker使用方法

BackgroundWorker:Argument-Object 的子代

C#中 BackGroundWorker与Thread的区别?

一走进多线程

BackgroundWorker的使用一二(可视化编程,开始后台工作,报告进度,取消后台工作等)