C# 并行处理使UI结巴是正常的吗

C# 并行处理使UI结巴是正常的吗,c#,.net,winforms,async-await,parallel.foreach,C#,.net,Winforms,Async Await,Parallel.foreach,我有一个Windows窗体应用程序,可以在GEDCOM系谱文件中复制和重新调整图像引用的大小。用户从主窗体中选择文件和输出目录以及重新调整大小选项,然后打开另一个窗体作为对话框,其中包含标签、进度条和按钮。我正在更新应用程序以使用.NET4.5中的新异步功能,并对其进行修改以使用并行处理。一切正常,除了我注意到UI响应有点不稳定(口吃);如果我不使用百分比更新消息标签,那么它会更平滑。另外,当我取消任务时,UI将挂起1到15秒。这个应用程序只供我个人使用,所以没什么大不了的,但我很好奇是什么导致

我有一个Windows窗体应用程序,可以在GEDCOM系谱文件中复制和重新调整图像引用的大小。用户从主窗体中选择文件和输出目录以及重新调整大小选项,然后打开另一个窗体作为对话框,其中包含标签、进度条和按钮。我正在更新应用程序以使用.NET4.5中的新异步功能,并对其进行修改以使用并行处理。一切正常,除了我注意到UI响应有点不稳定(口吃);如果我不使用百分比更新消息标签,那么它会更平滑。另外,当我取消任务时,UI将挂起1到15秒。这个应用程序只供我个人使用,所以没什么大不了的,但我很好奇是什么导致了这个问题,以及建议用什么方法来处理它。并行处理是否只是因为有太多线程要处理而使CPU过载?我尝试在每个循环迭代中添加一个Thread.Sleep(100),这似乎有点帮助

下面是仍然导致问题的应用程序的最低版本。复制:

  • 使用以下表单创建新的windows窗体应用程序
  • 创建一个包含大量jpeg图像(50多幅图像)的目录
  • 用目录替换_SourceDirectoryPath和_DestinationDirectoryPath变量
  • 运行应用程序
  • Designer.cs:

    partial class Form1
    {
        /// <summary>
        /// Required designer variable.
        /// </summary>
        private System.ComponentModel.IContainer components = null;
    
        /// <summary>
        /// Clean up any resources being used.
        /// </summary>
        /// <param name="disposing">true if managed resources should be disposed; otherwise, false.</param>
        protected override void Dispose(bool disposing)
        {
            if (disposing && (components != null))
            {
                components.Dispose();
            }
            base.Dispose(disposing);
        }
    
        #region Windows Form Designer generated code
    
        /// <summary>
        /// Required method for Designer support - do not modify
        /// the contents of this method with the code editor.
        /// </summary>
        private void InitializeComponent()
        {
            this.lblMessage = new System.Windows.Forms.Label();
            this.pgProgressBar = new System.Windows.Forms.ProgressBar();
            this.btnStart = new System.Windows.Forms.Button();
            this.btnCancel = new System.Windows.Forms.Button();
            this.SuspendLayout();
            // 
            // lblMessage
            // 
            this.lblMessage.AutoSize = true;
            this.lblMessage.Location = new System.Drawing.Point(32, 25);
            this.lblMessage.Name = "lblMessage";
            this.lblMessage.Size = new System.Drawing.Size(0, 13);
            this.lblMessage.TabIndex = 0;
            // 
            // pgProgressBar
            // 
            this.pgProgressBar.Location = new System.Drawing.Point(35, 51);
            this.pgProgressBar.Name = "pgProgressBar";
            this.pgProgressBar.Size = new System.Drawing.Size(253, 23);
            this.pgProgressBar.TabIndex = 1;
            // 
            // btnStart
            // 
            this.btnStart.Location = new System.Drawing.Point(132, 97);
            this.btnStart.Name = "btnStart";
            this.btnStart.Size = new System.Drawing.Size(75, 23);
            this.btnStart.TabIndex = 2;
            this.btnStart.Text = "Start";
            this.btnStart.UseVisualStyleBackColor = true;
            this.btnStart.Click += new System.EventHandler(this.btnStart_Click);
            // 
            // btnCancel
            // 
            this.btnCancel.Location = new System.Drawing.Point(213, 97);
            this.btnCancel.Name = "btnCancel";
            this.btnCancel.Size = new System.Drawing.Size(75, 23);
            this.btnCancel.TabIndex = 3;
            this.btnCancel.Text = "Cancel";
            this.btnCancel.UseVisualStyleBackColor = true;
            this.btnCancel.Click += new System.EventHandler(this.btnCancel_Click);
            // 
            // Form1
            // 
            this.AutoScaleDimensions = new System.Drawing.SizeF(6F, 13F);
            this.AutoScaleMode = System.Windows.Forms.AutoScaleMode.Font;
            this.ClientSize = new System.Drawing.Size(315, 149);
            this.Controls.Add(this.btnCancel);
            this.Controls.Add(this.btnStart);
            this.Controls.Add(this.pgProgressBar);
            this.Controls.Add(this.lblMessage);
            this.Name = "Form1";
            this.Text = "Form1";
            this.ResumeLayout(false);
            this.PerformLayout();
    
        }
    
        #endregion
    
        private System.Windows.Forms.Label lblMessage;
        private System.Windows.Forms.ProgressBar pgProgressBar;
        private System.Windows.Forms.Button btnStart;
        private System.Windows.Forms.Button btnCancel;
    }
    
    部分类表单1
    {
    /// 
    ///必需的设计器变量。
    /// 
    private System.ComponentModel.IContainer components=null;
    /// 
    ///清理所有正在使用的资源。
    /// 
    ///如果应释放托管资源,则为true;否则为false。
    受保护的覆盖无效处置(布尔处置)
    {
    if(处理和(组件!=null))
    {
    组件。Dispose();
    }
    基地。处置(处置);
    }
    #区域Windows窗体设计器生成的代码
    /// 
    ///设计器支持所需的方法-不修改
    ///此方法的内容与代码编辑器一起使用。
    /// 
    私有void InitializeComponent()
    {
    this.lblMessage=new System.Windows.Forms.Label();
    this.pgProgressBar=new System.Windows.Forms.ProgressBar();
    this.btnStart=new System.Windows.Forms.Button();
    this.btnCancel=new System.Windows.Forms.Button();
    这个.SuspendLayout();
    // 
    //LBL消息
    // 
    this.lblMessage.AutoSize=true;
    this.lblMessage.Location=新系统.图纸.点(32,25);
    this.lblMessage.Name=“lblMessage”;
    this.lblMessage.Size=新系统图纸尺寸(0,13);
    this.lblMessage.TabIndex=0;
    // 
    //pgProgressBar
    // 
    this.pgProgressBar.Location=新系统图纸点(35,51);
    this.pgProgressBar.Name=“pgProgressBar”;
    this.pgProgressBar.Size=新系统图纸尺寸(253,23);
    this.pgProgressBar.TabIndex=1;
    // 
    //btnStart
    // 
    this.btnStart.Location=新系统图点(132,97);
    this.btnStart.Name=“btnStart”;
    this.btnStart.Size=新系统.图纸.尺寸(75,23);
    this.btnStart.TabIndex=2;
    this.btnStart.Text=“开始”;
    this.btnStart.UseVisualStyleBackColor=true;
    this.btnStart.Click+=新建System.EventHandler(this.btnStart\u Click);
    // 
    //BTNCENCEL
    // 
    this.btnCancel.Location=新系统图纸点(213,97);
    this.btnCancel.Name=“btnCancel”;
    this.btnCancel.Size=新系统图纸尺寸(75,23);
    this.btnCancel.TabIndex=3;
    this.btnCancel.Text=“取消”;
    this.btnCancel.UseVisualStyleBackColor=true;
    this.btnCancel.Click+=新建System.EventHandler(this.btnCancel\u Click);
    // 
    //表格1
    // 
    此.AutoScaleDimensions=新系统.Drawing.SizeF(6F,13F);
    this.AutoScaleMode=System.Windows.Forms.AutoScaleMode.Font;
    this.ClientSize=新系统图尺寸(315149);
    this.Controls.Add(this.btnCancel);
    this.Controls.Add(this.btnStart);
    this.Controls.Add(this.pgProgressBar);
    this.Controls.Add(this.lblMessage);
    this.Name=“Form1”;
    this.Text=“Form1”;
    此选项为.resume布局(false);
    这个。执行布局();
    }
    #端区
    private System.Windows.Forms.Label lbl消息;
    private System.Windows.Forms.ProgressBar pgProgressBar;
    private System.Windows.Forms.Button btnStart;
    private System.Windows.Forms.Button btnCancel;
    }
    
    代码:

    使用系统;
    使用System.Collections.Generic;
    使用系统图;
    使用System.IO;
    使用System.Linq;
    使用系统线程;
    使用System.Threading.Tasks;
    使用System.Windows.Forms;
    公共部分类Form1:Form
    {
    私有CancellationTokenSource\u CancelSource;
    私有字符串\u SourceDirectoryPath=@“Your\Source\Directory”;
    私有字符串\u DestinationDirectoryPath=@“Your\Destination\Directory”;
    公共表格1()
    {
    初始化组件();
    lblMessage.Text=“单击开始开始提取图像”;
    btnCancel.Enabled=false;
    _CancelSource=新的CancellationTokenSource();
    }
    私有异步void btnStart\u单击(对象发送方,事件参数e)
    {
    btnStart.Enabled=false;
    btnCancel.Enabled=真;
    List files=wait Task.Run(()=>Directory.GetFiles(_SourceDirectoryPath,*.jpg”).ToList();
    //扫描/提取文件
    进度=新进度(UpdateProgress);
    int result=wait Task.Run(()=>ExtractFiles(文件,进度,_CancelSource.Token));
    如果(_CancelSource.IsCancellationRequested)
    {
    lblMessage.Text=“提取已由用户取消。”;
    }
    其他的
    {
    lblMessage.Text
    
    using System;
    using System.Collections.Generic;
    using System.Drawing;
    using System.IO;
    using System.Linq;
    using System.Threading;
    using System.Threading.Tasks;
    using System.Windows.Forms;
    
    public partial class Form1 : Form
    {
        private CancellationTokenSource _CancelSource;
        private string _SourceDirectoryPath = @"Your\Source\Directory";
        private string _DestinationDirectoryPath = @"Your\Destination\Directory";
    
        public Form1()
        {
            InitializeComponent();
            lblMessage.Text = "Click Start to begin extracting images";
            btnCancel.Enabled = false;
            _CancelSource = new CancellationTokenSource();
        }
    
        private async void btnStart_Click(object sender, EventArgs e)
        {
            btnStart.Enabled = false;
            btnCancel.Enabled = true;
    
            List<string> files = await Task.Run(() => Directory.GetFiles(_SourceDirectoryPath, "*.jpg").ToList());
    
            // scan/extract files
            Progress<int> progress = new Progress<int>(UpdateProgress);
            int result = await Task.Run(() => ExtractFiles(files, progress, _CancelSource.Token));
    
            if (_CancelSource.IsCancellationRequested)
            {
                lblMessage.Text = "Extraction cancelled by user.";
            }
            else
            {
                lblMessage.Text = string.Format("Extraction Complete: {0} files extracted.", result);
            }
            btnStart.Enabled = true;
            btnCancel.Enabled = false;
        }
    
        private void btnCancel_Click(object sender, EventArgs e)
        {
            lblMessage.Text = "Cancelling...";
            btnCancel.Enabled = false;
            _CancelSource.Cancel();
        }
    
        private void UpdateProgress(int value)
        {
            lblMessage.Text = string.Format("Extracting files: {0}%", value);
            pgProgressBar.Value = value;
        }
    
        public int ExtractFiles(List<string> fileReferences, IProgress<int> progress, CancellationToken cancelToken)
        {
            double totalFiles = fileReferences.Count;
            int processedCount = 0;
            int extractedCount = 0;
            int previousPercent = 0;
            Directory.CreateDirectory(_DestinationDirectoryPath);
    
            Parallel.ForEach(fileReferences, (reference, state) =>
            {
                if (cancelToken.IsCancellationRequested)
                {
                    state.Break();
                }
    
                string fileName = Path.GetFileName(reference);
                string filePath = Path.Combine(_DestinationDirectoryPath, fileName);
    
                using (Image image = Image.FromFile(reference))
                {
                    using (Image newImage = ResizeImage(image, 1000, 1000))
                    {
                        newImage.Save(filePath);
                        Interlocked.Increment(ref extractedCount);
                    }
                }
    
                Interlocked.Increment(ref processedCount);
                int percent = (int)(processedCount / totalFiles * 100);
                if (percent > previousPercent)
                {
                    progress.Report(percent);
                    Interlocked.Exchange(ref previousPercent, percent);
                }
            });
    
            return extractedCount;
        }
    
        public Image ResizeImage(Image image, int maxWidth, int maxHeight)
        {
            Image newImage = null;
    
            if (image.Width > maxWidth || image.Height > maxHeight)
            {
                double widthRatio = (double)maxWidth / (double)image.Width;
                double heightRatio = (double)maxHeight / (double)image.Height;
                double ratio = Math.Min(widthRatio, heightRatio);
                int newWidth = (int)(image.Width * ratio);
                int newHeight = (int)(image.Height * ratio);
    
                newImage = new Bitmap(newWidth, newHeight);
                using (Graphics graphic = Graphics.FromImage(newImage))
                {
                    graphic.CompositingQuality = System.Drawing.Drawing2D.CompositingQuality.HighQuality;
                    graphic.InterpolationMode = System.Drawing.Drawing2D.InterpolationMode.HighQualityBicubic;
                    graphic.SmoothingMode = System.Drawing.Drawing2D.SmoothingMode.HighQuality;
                    graphic.DrawImage(image, 0, 0, newWidth, newHeight);
                }
            }
    
            return newImage;
        }
    }