C# 将BackgroundWorker转换为异步

C# 将BackgroundWorker转换为异步,c#,asynchronous,async-await,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_Listbo

我曾经厌倦使用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();            
}
在阅读了更多内容后,我开始使用Task.Run()

在->


在这里->

原始代码一次只处理一个文件,因此您可以使用一个简单的循环,只异步执行
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
,使它*不阻塞,那就更好了。我想它是用来阻挡的。方法应该异步等待,而不是侦听事件。此类事件可以转换为任务,如中所示

该方法将如下所示:

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;
}

我会考虑用微软的反应框架来做这件事。我认为它比使用任务更强大

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");
            });
}

只需获取“System.Reactive.Windows.Forms”,并使用System.Reactive.Linq添加
让它工作。

检查这个答案:我使用了任务、功能和动作来实现它。看看这个。ShellandWait是做什么的?既然您运行了外部进程,为什么要阻止等待它们中的每一个呢?您也可以在
vb.net
中做同样的事情……我正在UI线程之外同步运行ShellandWait。我通常运行的进程是长时间运行且CPU密集型的(想想7zip)。同时运行它们会很快使系统崩溃,因为我一次运行数百到数千个进程。VB.NET可以做C所能做的一切。大多数文档都是针对C语言的,所以为了便于文档的编写,我做了一些尝试。这不是一个好的解决方案,而且太冗长了-
。Invoke()
将在UI线程上运行,那么为什么要将其放在任务中呢?如果要运行外部进程,为什么要使用此代码?默认情况下,这些进程将并行运行<应修改代码>ShellandWait
,使其不会阻塞。至于异步进度报告,已经有这样一个类,
progress
。检查是否有良好的描述。顺便说一句,Task.Run async的
更新进度条的答案是错误的,如果UI线程中有其他内容,则很容易导致死锁。正确且投票最多的答案使用进度。在执行任务之前,人们使用BeginInvoke而不是Invoke来避免UI线程繁忙时的阻塞和等待。这实际上非常简单。我已经将长时间运行的函数转换为使用它。我仍然可以在边缘情况下使用“wait Task.Run(()=>)。
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");
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;
}
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");
            });
}