如何编写一个异步类,使重复代码最少?

3

如何编写像WebClient一样的异步类?

有没有什么方法可以使它更简短,并且不必为每个方法重复编写?

例如,我有:

Download(string a)
Download(string a, string b)

我需要为每个Async + Complete方法都重新编写吗?

非常感谢。


我刚刚看到tommieb75的评论后意识到你已经标记了asp.net - 你是否计划从你的网站或服务中运行异步方法?如果是这样,我不确定这是否是你想要做的。但我可能错了...请详细解释一下,并告诉我们背景情况。 - Nick Bolton
具体来说,我指的是ASP.NET MVC,但总的来说,我认为它可以直接开箱即用,不是吗?如果这个线程能帮助所有寻找最佳异步处理方式的人,我会很高兴。 - DucDigital
4个回答

3

更新:

这个示例是针对winforms的,我相信OP正在问有关asp.net的问题。一旦我从我的评论中得到一些反馈,我将修改我的答案。


你猜对了。我发现在.Net中多线程代码的一种优雅的方式是使用BackgroundWorker类。与在本机C++中实现线程相比,它真的非常简单。

实际上,MSDN页面上的示例可能有点令人不知所措,你可以总结如下(伪代码-可能无法编译):

private BackgroundWorker bw;

private void foobar() {
  bw = new BackgroundWorker(); // should be called once, in ctor
  bw.DoWork += new DoWorkEventHandler(bw_DoWork);
  bw.RunWorkerCompleted += new RunWorkerCompletedEventHandler(bw_Completed);

  int i = 0;
  bw.RunWorkerAsync(i);
}

private void bw_DoWork(object sender, DoWorkEventArgs e) {
  int i = (int)e.Argument;
  i++;
  e.Result = i;
}

private void bw_Completed(object sender, RunWorkerCompletedEventArgs e) {
  if (e.Error != null) {
    MessageBox.Show(e.Error.Message);
  } else {
    int i = (int)e.Result;
    MessageBox.Show(i.ToString());
  }
}

关于最小化重复代码,你可以使用同一后台工作线程来处理所有方法,但是这可能会导致代码变得冗长而难以维护。如果只有几个方法,那么为每个操作创建事件处理程序不应该显得太杂乱。


我不明白,如果这个标签是'asp.net',那么怎么可能有一个消息框,或者确实有一个针对'asp.net'的实际消息框吗...BackgroundWorker更适用于WinForms... - t0mm13b
哎呀!我当时在想什么啊!?嗯,你说得对,BackgroundWorker 对于 Asp.Net 来说并不是很有用。我会更新我的答案,提供一个更合适的例子。 - Nick Bolton

1

听起来你在询问实现一个类,该类公开具有异步版本方法的最佳实践,类似于WebClient的方式?

值得注意的是,在.NET框架中通常使用两种模式来支持异步使用的类。例如,您通常会看到像Stream这样的类,它们公开了BeginRead和EndRead。它们使用IAsyncResult并且往往更复杂。(MSDN上的概述)

然后稍后他们提出了基于事件的异步模式,例如WebClient使用的模式,其中您有一个DownloadString/DownloadStringAsync方法和相应的DownloadStringCompleted事件。对于简单情况,这更易于使用,但在我看来不如Begin/End模式灵活。

因此,在您的示例中,您有两个重载的方法。在这种情况下,我会简单地设计它们,使得具有较少参数的方法推迟到具有更多参数的方法,根据需要传递默认值或null。您可以有多个Download/DownloadAsync的重载,但只能有一个DownloadCompleted事件,这没问题。

这里是一个非常基本的实现。请注意,唯一真正执行任何工作的方法是同步下载方法。还要注意的是,这并不是最有效的方法。如果您想利用HttpWebRequest等异步IO功能,这个示例将很快变得复杂。还有一个过于复杂的异步操作模式,我从来没有喜欢过。
class Downloader {

    public void Download(string url, string localPath) {
        if (localPath == null) {
            localPath = Environment.CurrentDirectory;
        }
        // implement blocking download code
    }

    public void Download(string url) {
        Download(url, null);
    }

    public void DownloadAsync(string url, string localPath) {

        ThreadPool.QueueUserWorkItem( state => {

            // call the sync version using your favorite
            // threading api (threadpool, tasks, delegate.begininvoke, etc)
            Download(url, localPath);

            // synchronizationcontext lets us raise the event back on
            // the UI thread without worrying about winforms vs wpf, etc
            SynchronizationContext.Current.Post( OnDownloadCompleted, null );

        });

    }

    public void DownloadAsync(string url) {
        DownloadAsync(url, null);
    }

    private void OnDownloadCompleted(object state) {
        var handler = DownloadCompleted;
        if (handler != null) {
            handler(this, EventArgs.Empty);
        }
    }

    public event EventHandler DownloadCompleted;

}

1
如果您想将同步方法异步调用,您需要使用委托。 http://msdn.microsoft.com/en-us/library/2e08f6yc.aspx。通过这种方法,您不必创建Async和Complete方法。
您所描述的模式是使用IAsyncResult,这种情况下,您需要实现IAsyncResult(根据您的要求,也可以使用Framework中的一个)。在您的类中派生它并像您提到的那样实现Async和Complete方法。

0
使用 MethodInvoker.BeginInvoke 来调用一个新的线程。

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