C#中的异步方法

3
我有几个需要在Windows Forms应用程序的后台运行的进程,因为它们需要太长时间,我不想冻结用户界面直到它们完全完成。我希望有一个指示器来显示每个操作的进度,目前我有一个窗体来显示每个操作的进度,但我的操作是同步运行的。
所以我的问题是,最简单的异步运行这些操作(访问数据库)的方法是什么?
我忘记了应用程序需要的一个重要功能,即用户随时可以取消任何操作的选项。我认为这个要求会使应用程序变得更加复杂,至少对于我的当前技能水平来说是这样的,所以基本上我想强调我需要一个易于理解和实现的解决方案。我知道有一些好的实践方法需要遵循,但目前我需要一些可工作的代码,以后我会有更多的时间来重构代码。
3个回答

3

.NET 4增加了任务并行库,它提供了一种非常简洁的机制,可以将同步操作转换为异步操作。

它允许您将同步操作包装到任务中,然后可以等待该任务完成,或者使用继续操作(当任务完成时执行的某些代码)。

通常会看起来像这样:

Task processTask = Task.Factory.StartNew(() => YourProcess(foo, bar));

一旦您获得了任务,您有很多选择,包括阻止:

// Do other work, then:
processTask.Wait(); // This blocks until the task is completed

或者,如果你想要一个继续执行的代码(当它完成时):

processTask.ContinueWith( t => ProcessCompletionMethod());

你还可以使用这个方法来组合多个异步操作,当它们中的任何一个或全部完成时就完成等等。
请注意,以这种方式使用TaskTask<T>还有另一个巨大的优势——如果您稍后迁移到.NET 4.5,您的API将按原样工作,不需要代码更改,并且支持C# 5中的新异步/等待语言特性。

我忘记了应用程序需要的一个重要功能,用户将有选择在任何时候取消任何操作的选项。

TPL的设计初衷也是为了与.NET 4中的新协作式取消模型很好地配合使用。这使您可以拥有一个CancellationTokenSource,可用于取消您的任何或所有任务。

2

1
BackgroundWorker可以完成工作,但在处理异步操作时存在一些巨大的缺点。它更难处理多个任务(例如:当所有任务都完成时运行某些内容)。 - Reed Copsey
相反,与BackgroundWorker相比,使用任务进行进度报告更加困难。 - dtb
我不太同意 - 如果从 UI 上下文创建 TaskScheduler,报告可能会更容易,因为您不必将要报告的信息映射到可通过 ReportProgress 事件传递的内容。"Reporting" 只是安排另一个任务。 - Reed Copsey
虽然BW在报告方面很不错,特别是对于简单的“报告”,比如进度条;) - Reed Copsey

2
在C#中,有几种方法可以实现这一点。
个人建议您尝试使用响应式扩展。

http://msdn.microsoft.com/en-us/data/gg577609.aspx

你实际上可以做这样的事情:

https://stackoverflow.com/a/10804404/1268570

我为您创建了这个,虽然它不是线程安全的,但这将是一个很好的起点。在一个表单中。
var a = Observable.Start(() => Thread.Sleep(8000)).StartAsync(CancellationToken.None);
var b = Observable.Start(() => Thread.Sleep(15000)).StartAsync(CancellationToken.None);
var c = Observable.Start(() => Thread.Sleep(3000)).StartAsync(CancellationToken.None);

Manager.Add("a", a.ObserveOn(this).Subscribe(x => MessageBox.Show("a done")));
Manager.Add("b", b.ObserveOn(this).Subscribe(x => MessageBox.Show("b done")));
Manager.Add("c", c.ObserveOn(this).Subscribe(x => MessageBox.Show("c done")));

private void button1_Click(object sender, EventArgs e)
{
    Manager.Cancel("b");
}

经理实用工具
public static class Manager
{
    private static IDictionary<string, IDisposable> runningOperations;

    static Manager()
    {
        runningOperations = new Dictionary<string, IDisposable>();
    }

    public static void Add(string key, IDisposable runningOperation)
    {
        if (runningOperations.ContainsKey(key))
        {
            throw new ArgumentOutOfRangeException("key");
        }

        runningOperations.Add(key, runningOperation);
    }

    public static void Cancel(string key)
    {
        IDisposable value = null;
        if (runningOperations.TryGetValue(key, out value))
        {
            value.Dispose();
            runningOperations.Remove(key);
        }
    }

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接