将BackgroundWorker转换为异步

时间:2018-06-27 17:14:17

标签: c# asynchronous async-await

过去我对使用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(); }
}

3 个答案:

答案 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))

在-> Update progress bar from Task.Run async

中找到进度条的答案

在这里-> How to Invoke the progress bar in Status strip?

答案 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;即可使其正常工作。