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