【发布时间】:2015-04-17 22:24:57
【问题描述】:
我目前正在开发一个将文件列表从 .ps (PostScript) 转换为 .png 的程序。
最初,这是在一个批处理文件中完成的,一次一个文件。我正在编写使用 Ghostscript.NET dll 异步处理这些文件的代码。通过将这些拆分为任务,我将处理时间从 30 分钟缩短到了大约 6 分钟。
我希望能够向用户展示这方面的一些进展,这样我的程序就不会看起来像被冻结了。
我对线程的了解足以让自己感到沮丧,因此非常感谢有关最佳方法的任何建议。下面的代码实现了一个 BackgroundWorker 来尝试显示进度。我以前用过 BGWorker 来显示进度,但不是在这样的多个任务上。事实上,这是我第一次不只使用 BGWorker 的多线程。
我觉得 BGWorker 可能不是我需要使用的,但我想在我问之前尝试自己尝试一下。
这是我目前的代码:
public partial class ProcessStatusForm : Form
{
public string[] testList;
public string wordPath;
public string StatusText;
public GhostscriptVersionInfo _gs_version_info;
public DirectoryInfo dInfo;
public List<Task> tasks;
public float NumberOfTasks;
public bool PS2PNGRunning;
public int ProgressPct;
public float dPercent;
public decimal decPercent;
public ProcessStatusForm(string wordDoc, List<string> runList)
{
InitializeComponent();
this.wordPath = wordDoc;
this.testList = runList.ToArray();
this.StatusText = string.Empty;
this._gs_version_info = GhostscriptVersionInfo.GetLastInstalledVersion(GhostscriptLicense.GPL |
GhostscriptLicense.AFPL, GhostscriptLicense.GPL);
this.dInfo = new DirectoryInfo(SettingsClass.PSFolder);
this.PS2PNGRunning = false;
this.ProgressPct = 0;
this.NumberOfTasks = runList.Count;
}
private void ProcessStatusForm_Shown(object sender, EventArgs e)
{
//Spawn tasks for each of the .ps files in the PS_FILES folder
tasks = new List<Task>(dInfo.GetFiles("*.ps").Length);
//Start the BackgroundWorker
this.PS2PNGRunning = true;
BackgroundWorker.RunWorkerAsync();
foreach (var file in dInfo.GetFiles("*.ps"))
{
//Get fileName to pass fo the ConvertPS2PNG
string inputFile = file.Name;
//Create the Task
var task = Task.Factory.StartNew(() => ConvertPS2PNG(inputFile));
tasks.Add(task);
}
//Wait until all tasks have completed
Task.WaitAll(tasks.ToArray());
PS2PNGRunning = false;
}
private void ConvertPS2PNG(string input)
{
string output = input.Replace(".ps", "_01.png");
input = SettingsClass.PSFolder + input;
output = SettingsClass.PNGFolder + output;
GhostscriptProcessor processor = new GhostscriptProcessor(_gs_version_info, true);
processor.Process(CreateGSArgs(input, output), new ConsoleStdIO(true, true, true));
}
private void BackgroundWorker_DoWork(object sender, DoWorkEventArgs e)
{
ProgressPct = 0;
while (PS2PNGRunning)
{
Thread.Sleep(1000);
float TasksCompleted = 0;
foreach (var tsk in tasks)
{
if (tsk.Status == TaskStatus.RanToCompletion)
{
TasksCompleted++;
}
}
StatusText = TasksCompleted + " of " + NumberOfTasks + " converted...";
dPercent = TasksCompleted / NumberOfTasks;
dPercent *= 100;
decPercent = (decimal)dPercent;
decPercent = Math.Round(decPercent);
ProgressPct = (int)decPercent;
BackgroundWorker.ReportProgress(ProgressPct);
}
BackgroundWorker.ReportProgress(100);
}
private void BackgroundWorker_ProgressChanged(object sender, ProgressChangedEventArgs e)
{
this.ProgressLabel.Text = this.StatusText;
this.progressBar.Style = ProgressBarStyle.Continuous;
this.progressBar.Value = e.ProgressPercentage;
}
public string[] CreateGSArgs(string inPath, string outPath)
{
List<string> gsArgs = new List<string>();
gsArgs.Add("-dBATCH");
gsArgs.Add("-dNOPAUSE");
gsArgs.Add("-sDEVICE=png16m");
gsArgs.Add("-dQUIET");
gsArgs.Add("-sPAPERSIZE=letter");
gsArgs.Add("-r800");
gsArgs.Add("-sOutputFile=" + outPath);
gsArgs.Add(inPath);
return gsArgs.ToArray();
}
}
当我在 BackgroundWorker_DoWork 的代码中添加中断时,似乎一切正常,但是当它到达 BackgroundWorker.ReportProgress() 时,它永远不会到达 BackgroundWorker_ProgressChanged() 方法。
至少,我可以忍受在运行时将 progressBar.Style 作为选框,以便用户可以看到程序正在运行,但报告实际进度将是理想的。
正如我之前所说,我没有在线程方面做大量工作,我在这个主题上的所有知识几乎都来自 Google 和 StackOverflow。如果有完全不同的方法可以做到这一点,我愿意接受所有批评。
【问题讨论】:
-
您的 UI 使用的是什么?您使用的是 Winforms 还是 WPF?
-
我只是为此使用 WinForms。如果有更好或更简单的方法可以使用 WPF,我可以使用 WPF,因为我刚开始开发它。
-
您可能希望设置一个调度计时器来保持 UI 更新而不是该事件。要么那个,要么事件可能没有被引发,所以你可以尝试从窗口的调度程序中对进度等进行更改:
this.Dispatcher.Invoke(new Action(() => {"Do work here""}));这应该会引发对 UI 的这些更改。 -
我以前从未使用过 Dispatcher。我需要更改为 WPF 才能使用它吗?我在该主题上看到的所有材料似乎都是通过 WPF 完成的。
-
它通常在 WPF 和 WPF 的上下文中使用,在我看来,它确实提供了比 Winforms 更大的灵活性,但 Dispatcher 的使用应该独立于 WPF 窗口。对 Dispatcher 做一些研究,文档将提供大量更详细的信息。
标签: c# multithreading task-parallel-library task ghostscript.net