过去我对使用BackgroundWorker感到厌倦,因为它需要很多功能才能正常工作。但是,当我从VB.NET交换到C#时(大约一个月前),我偶然发现了一种非常简单的实例化方法;
示例;
private void cmdMaxCompressPNG_Click(object sender, EventArgs e) {
pbStatus.Maximum = lstFiles.Items.Count;
List<string> FileList = Load_Listbox_Data();
var bw = new BackgroundWorker();
bw.WorkerReportsProgress = true;
bw.DoWork += delegate {
foreach (string FileName in FileList) {
ShellandWait("optipng.exe", String.Format("\"{0}\"", FileName));
bw.ReportProgress(1);
}
};
bw.ProgressChanged += (object s, ProgressChangedEventArgs ex) => {
pbStatus.Value += 1;
};
bw.RunWorkerCompleted += delegate {
lstFiles.Items.Clear();
pbStatus.Value = 0;
MessageBox.Show(text: "Task Complete", caption: "Status Update");
};
bw.RunWorkerAsync();
}
一站式提供所有功能!编写简单,易于理解,并且没有实际的工作原理。我什至用它制作了一个片段。从那以后,我已经将所有由多个部分组成的BackgroundWorker函数都转换为这段小巧的代码。与过去相比,我也开始更自由地使用它们。昨天我正在阅读一篇有关异步和等待的文章,这显然是我应该如何做的事情。我的头缠不住了。
我尝试使用局部函数,但是措辞不正确。它一直试图使其保持同步。
我如何将以上内容转换为同样紧密的Await / Async逻辑实现?
[编辑]
ShellandWait;
private void ShellandWait(string ProcessPath, string Arguments, bool boolWait = true) {
System.Diagnostics.Process ShellProcess = new System.Diagnostics.Process();
ShellProcess.StartInfo.FileName = ProcessPath;
ShellProcess.StartInfo.WindowStyle = System.Diagnostics.ProcessWindowStyle.Hidden;
ShellProcess.StartInfo.Arguments = Arguments;
ShellProcess.StartInfo.CreateNoWindow = true;
ShellProcess.StartInfo.UseShellExecute = false;
ShellProcess.StartInfo.RedirectStandardOutput = true;
ShellProcess.Start();
if (boolWait) { ShellProcess.WaitForExit(); }
if (boolWait) { ShellProcess.Close(); }
}
答案 0 :(得分:1)
原始代码一次只处理一个文件,因此您可以使用一个简单的循环并仅异步执行ShellandAwait
:
private void cmdMaxCompressPNG_Click(object sender, EventArgs e)
{
pbStatus.Maximum = lstFiles.Items.Count;
var FileList = Load_Listbox_Data();
foreach (var FileName in FileList)
{
//Only thing that needs to run in the background
await Task.Run(()=>ShellandWait("optipng.exe", String.Format("\"{0}\"", FileName));
//Back in the UI
pbStatus.Value += 1;
}
};
lstFiles.Items.Clear();
pbStatus.Value = 0;
MessageBox.Show(text: "Task Complete", caption: "Status Update");
如果将ShellandWait
修改为*不会阻塞,那就更好了。我假设它使用Process.WaitForExit()进行阻止。该方法应该异步等待,而不是通过监听Exited事件。此类事件可以转换为Tasks and the Event-based Asynchronous Pattern中所示的任务。
该方法看起来像这样:
Task<string> ShellAsync(string commandPath,string argument)
{
var tcs = new TaskCompletionSource<string>();
var process = new Process();
//Configure the process
//...
process.EnableRaisingEvents = true;
process.Exited += (s,e) => tcs.TrySetResult(argument);
process.Start();
return tcs.Task;
}
这将使循环简化为:
foreach (var FileName in FileList)
{
await ShellAsync("optipng.exe", String.Format("\"{0}\"", FileName));
//Back in the UI
pbStatus.Value += 1;
}
答案 1 :(得分:0)
我读完这篇文章后的方法是使用Task.Run()
private async void cmdMaxCompressPNG_Click(object sender, EventArgs e) {
pbStatus.Maximum = lstFiles.Items.Count;
List<string> FileList = Load_Listbox_Data();
await Task.Run(() => {
foreach (string FileName in FileList) {
ShellandWait("optipng.exe", String.Format("\"{0}\"", FileName));
pbStatus.GetCurrentParent().Invoke(new MethodInvoker(delegate { pbStatus.Value += 1; }));
}
});
lstFiles.Items.Clear();
pbStatus.Value = 0;
MessageBox.Show(text: "Task Complete", caption: "Status Update");
}
请注意私有旁边的异步。
由于它是一个状态条进度条,所以我不得不看上进度条。如果可以的话,我可以使用它;
pbStatus.Invoke((Action)(() => pbStatus.Value += 1))
中找到进度条的答案
答案 2 :(得分:0)
我会考虑使用Microsoft的Reactive Framework进行此操作。我认为它比使用Tasks强大得多。
private void cmdMaxCompressPNG_Click(object sender, EventArgs e)
{
pbStatus.Maximum = lstFiles.Items.Count;
var query =
from FileName in Load_Listbox_Data().ToObservable()
from u in Observable.Start(() =>
System.Diagnostics.Process
.Start("optipng.exe", String.Format("\"{0}\"", FileName))
.WaitForExit())
select u;
query
.ObserveOn(this) //marshall back to UI thread
.Subscribe(
x => pbStatus.Value += 1,
() =>
{
lstFiles.Items.Clear();
pbStatus.Value = 0;
MessageBox.Show(text: "Task Complete", caption: "Status Update");
});
}
只需NuGet“ System.Reactive.Windows.Forms”并添加using System.Reactive.Linq;
即可使其正常工作。