我目前正在开发一个将文件列表从 .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。如果有完全不同的方法可以做到这一点,我愿意接受所有批评。