如何使异步任务同步

时间:2012-04-19 14:46:32

标签: c# windows-phone-7

我试图从两个不同的网站获取两种类型的数据并将其绑定到列表但是我遇到了Async的问题,我想要做的是从rss获取信息将其添加到列表中,然后从另一个网站获取信息将其添加到列表然后将两个添加到绑定的可观察集合。但是DownloadStringAsync过度运行其他应用程序崩溃。你能帮帮我吗?

我的代码是

private static ObservableCollection<Top> top= new ObservableCollection<Top>();
    private static ObservableCollection<string> place= new ObservableCollection<string>();
    // Constructor
    public MainPage()
    {
        InitializeComponent();
        if (NetworkInterface.GetIsNetworkAvailable())
        {
            LoadSiteContent_A(url1);

           LoadSiteContent_B(url2);


        }
        else
            MessageBox.Show("No Internet Connection, please connect to use this applacation");



        listBox.ItemsSource = top; //trying to bind listbox after web calls
    }





    public void LoadSiteContent_A(string url)
    {

            //create a new WebClient object
            WebClient clientC = new WebClient();


            clientC.DownloadStringCompleted += new DownloadStringCompletedEventHandler(a_DownloadStringCompleted);
            clientC.DownloadStringAsync(new Uri(url));


    }

     public void LoadSiteContent_B(string url)
    {
            //create a new WebClient object
            WebClient clientC = new WebClient();


            clientC.DownloadStringCompleted += new DownloadStringCompletedEventHandler(b_DownloadStringCompleted);
            clientC.DownloadStringAsync(new Uri(url));


    }

     public void a_DownloadStringCompleted(Object sender, DownloadStringCompletedEventArgs e)
     {
         string testString = "";


         if (!e.Cancelled && e.Error == null)
         {
             string str;

             str = (string)e.Result;

             //Various operations and parsing


                     place.Add(testString);

          }


             }


     }
    public void b_DownloadStringCompleted(Object sender, DownloadStringCompletedEventArgs e)
    {

        string testMatch = "";


        if (!e.Cancelled && e.Error == null)
        {
            string str;
            // Size the control to fill the form with a margin
            str = (string)e.Result;

               //Various operations and parsing



                top.Add(new Top(testMatch,(place.Count+1)));


            }



    }


public class TopUsers
{
    public string TopUsername { get; set; }
    public int Place { get; set; }

    public TopUsers(string topusername, int place)
    {
        this.TopUsername = topusername;
        this.Place = place;

    }
}


}

4 个答案:

答案 0 :(得分:1)

这是一个替代答案(AlexTheo的解决方案应该有效)。

当他们向我们(WP开发者)提供新的异步内容时,所有这一切都变得容易了。

您的代码可以这样写:

public async MainPage()
{
    InitializeComponent();
    DoAsyncLoad();
}
private async Task DoAsyncLoad()  // note use of "async" keyword
{
   if (NetworkInterface.GetIsNetworkAvailable())
   {
        await LoadSiteContent_A("");
        await LoadSiteContent_B("");
   }
   else
        MessageBox.Show("No Internet Connection, please connect to use this applacation");

   listBox.ItemsSource = top; //trying to bind listbox after web calls
}

public async Task LoadSiteContent_A(string url)
{
     //create a new WebClient object
     WebClient clientC = new WebClient();

     var result = await clientC.DownloadStringTaskAsync(new Uri(url));
     // No need for a Lambda or setting up an event

     var testString = result; // result is the string you were waiting for (will be empty of canceled or errored) 
}
public async Task LoadSiteContent_B(string url)
{
     //create a new WebClient object
     WebClient clientC = new WebClient();

     var result = await clientC.DownloadStringTaskAsync(new Uri(url));
     // Again, no need for a Lambda or setting up an event (code is simpler as a result)
     top.Add(new Top(testMatch, place.Count + 1));
 }

您需要进行更多代码更改(使用Http调用的异步版本并将LoadSiteContent_A / B标记为异步 - 并设置任务返回)。

BTW,您实际上可以加载最新的Async-CTP3并发布以这种方式编写的WP代码。大多数人都对CTP有点害怕。

我写了一篇博文,你可以在这里查看 - http://www.jaykimble.net/metro-nuggets-async-is-your-friend.aspx

答案 1 :(得分:1)

我不会试图让它们一个接一个地做到这一点。通过“堆叠”它们一个接一个,就像你首先失去了异步调用的所有优点一样。不仅如此,而且在像Windows Phone这样的移动平台上,您必须记住网络呼叫排队等待有效使用天线。当你同时进行两个呼叫时,它们在相同的天线连接中被执行的可能性要高得多,这是一个“好事”。

接下来,每个回调实际上都是更新完全独立的集合。 A正在更新place集合,而B正在更新top集合。所以这两个问题不是以任何方式相互踩踏的问题。

我在这里看到的唯一真正问题就是您正在更新设置为top的{​​{1}}集合。您需要将绑定数据的更新封送回UI线程(也称为listBox.ItemsSource线程),以便绑定到它们的控件将在正确的线程上更新。

因此,您应该对您的任何代码进行的唯一更改是将新项目添加到Dispatcher集合,然后返回top中的Dispatcher主题打回来。这看起来像这样:

B

这样,除了将项目添加到public void b_DownloadStringCompleted(Object sender, DownloadStringCompletedEventArgs e) { string testMatch = ""; if(!e.Cancelled && e.Error == null) { string str; // Size the control to fill the form with a margin str = (string)e.Result; //Various operations and parsing Top newTop = new Top(testMatch,(place.Count+1)); Dispatcher.Invoke(() => { top.Add(newTop); }); } } 集合中的小部分外,您的所有工作都保持异步/并发。

答案 2 :(得分:0)

首先,我相信lamdba会比你的回调更好。 为了同步下载,您必须在LoadSiteContent_A的完整事件中调用LoadSiteContent_B。

private static ObservableCollection<Top> top= new ObservableCollection<Top>();
    private static ObservableCollection<string> place= new ObservableCollection<string>();
    private string _url1;
    private string _url2;
    // Constructor
    public MainPage(string url1, string url2)
    {
        InitializeComponent();
        if (NetworkInterface.GetIsNetworkAvailable())
        {
            _url1 = url1;
            _url2 = url2;
            LoadSiteContent_A(url1);
        }
        else
            MessageBox.Show("No Internet Connection, please connect to use this applacation");
        listBox.ItemsSource = top; //trying to bind listbox after web calls
    }

    public void LoadSiteContent_A(string url)
    {
            //create a new WebClient object
            WebClient clientC = new WebClient();
            clientC.DownloadStringCompleted += (sender, e) => { 
             string testString = "";
             if (!e.Cancelled && e.Error == null)
             {
                 string str;
                 str = (string)e.Result;
                 //Various operations and parsing
                place.Add(testString);
                LoadSiteContent_B(_url2);
              }
            };

            clientC.DownloadStringAsync(new Uri(url));
    }

    public void LoadSiteContent_B(string url)
    {
            //create a new WebClient object
            WebClient clientC = new WebClient();
            clientC.DownloadStringCompleted += (sender, e) => {/*do whatever you need*/};
            clientC.DownloadStringAsync(new Uri(url));
    }


public class TopUsers
{
    public string TopUsername { get; set; }
    public int Place { get; set; }

    public TopUsers(string topusername, int place)
    {
        this.TopUsername = topusername;
        this.Place = place;

    }
}


}

答案 3 :(得分:0)

即使使用lambda,也有一个更优雅的解决方案 - 使用自定义Action,其中T是数据类型。

例如:

public void LoadSiteContent_A(string url, Action<string> onCompletion)
{
        //create a new WebClient object
        WebClient clientC = new WebClient();

        clientC.DownloadStringCompleted += (s,e) =>
        {
             onCompletion(e.Result);
        };
        clientC.DownloadStringAsync(new Uri(url));
}

当您调用此方法时,您可以传递这样的操作:

LoadSiteContent_a(yourUrlWhatever, data =>
{
   // DO SOMETHING WITH DATA
});