【问题标题】:How do I write an async class with a minimal amount of repeated code?如何用最少的重复代码编写异步类?
【发布时间】:2010-01-10 23:53:53
【问题描述】:

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

有没有我可以保持简短并且不必为每种方法重复它?

例如,我有:

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

我是否必须为其中的每一个重写 Async + Complete 方法?

非常感谢。

【问题讨论】:

  • 在阅读 tommieb75 的评论后,我才意识到您已将其标记为 asp.net - 您是否打算从您的网站或服务运行异步方法?如果是这样,我不确定这是你想要做的。不过我可能是错的......请详细解释,并在背景中填写我们。
  • 具体来说,我的意思是asp.net mvc,但总的来说,我认为它可以开箱即用,不是吗?如果这个帖子可以帮助所有寻找最佳异步方式的人,我会很高兴。

标签: c# asp.net asp.net-mvc winforms asynchronous


【解决方案1】:

更新:

这个例子是针对winforms的,我相信OP是在询问asp.net。收到我的 cmets 的一些反馈后,我会修改我的答案。


你猜对了。我发现.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'的消息框......后台工作者更多对于winforms...
  • 哎呀!我在想什么!?嗯,你说得对,BackgroundWorker 对于Asp.Net 一点用处都没有。我会用一个更合适的例子来更新我的答案。
【解决方案2】:

如果您想异步调用同步方法,则需要使用委托。 http://msdn.microsoft.com/en-us/library/2e08f6yc.aspx。使用这种方法,您不必创建 Async 和 Complete 方法。

您描述的模式是使用 IAsyncResult,在这种情况下,您需要实现 IAsyncResult(您也可以根据您的要求使用框架中的那个)。在您的类中派生它并实现您提到的 Async 和 Complete 方法。

【讨论】:

    【解决方案3】:

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

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

    后来他们提出了event-based async pattern,例如 WebClient 使用的那个,你有一个 DownloadString/DownloadStringAsync 方法和一个相应的 DownloadStringCompleted 事件。对于简单的情况,这更容易使用,但在我看来不如 Begin/End 模式灵活。

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

    这是一个非常基本的实现。请注意,真正做任何工作的唯一方法是一个同步下载方法。同样重要的是要注意,这也不是最有效的方法。如果你想利用 HttpWebRequest 等的异步 IO 特性,这个例子会很快变得复杂。还有一个我一点都不喜欢的过于复杂的Async Operation 模式。

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

    【讨论】:

      【解决方案4】:

      使用 MethodInvoker.BeginInvoke 调用新的头。

      【讨论】:

        猜你喜欢
        • 1970-01-01
        • 2013-02-09
        • 1970-01-01
        • 1970-01-01
        • 2017-07-16
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        相关资源
        最近更新 更多