2012-04-19 2 views
0

Я пытаюсь получить два типа данных с двух разных сайтов и привязать их к списку, но у меня возникла проблема с Async, то, что я хочу сделать, это получить информацию от rss добавить его в список, затем получить информацию с другого веб-сайта, добавить его в список, а затем добавить два в связанную наблюдаемую коллекцию. Но DownloadStringAsync перегоняют друг друга, и приложение выходит из строя. Не могли бы вы мне помочь?Как сделать асинхронную задачу synchronus

моего код

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; 

    } 
} 


} 
+1

Если вы хотите, чтобы быть синхронными, то почему бы просто не назвать LoadSiteContent_B в a_DownloadStringCompleted()? Это будет имитировать синхронную активность, но с преимуществами асинхронности. – Archer

+0

Ты отлично справляешься с реализацией асинхронной работы. Вам просто нужно найти способ синхронного добавления к наблюдаемой коллекции. Подсказка: найдите ключевое слово «lock» – Polity

+0

Поцарапайте мой намек, вы используете статический экземпляр и, следовательно, должны переосмыслить свой дизайн. Добавление к вашей наблюдаемой коллекции должно выполняться в основном потоке. new Hint: синхронизация поиска. – Polity

ответ

0

Прежде всего, я считаю, что lamdba будет лучше, чем обратный вызов в вашем случае. Чтобы синхронизировать загрузку, вы должны вызвать LoadSiteContent_B в полном событии LoadSiteContent_A.

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; 

    } 
} 


} 
1

Это более альтернативный ответ (решение AlexTheo должно работать).

Все это становится намного легче, когда они дают нам (WP Developers) новый материал Async.

Ваш код может быть записан следующим образом:

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

Есть еще изменений коды вы должны сделать (с использованием версии ASync из Http вызовов и маркировки LoadSiteContent_A/B в качестве асинхронных -х установок возвращение Задачи).

Кстати, вы можете загрузить последнюю версию Async-CTP3 и разблокировать WP-код, написанный таким образом. Большинство людей немного боятся CTP.

я написал сообщение в блоге на это, что вы можете проверить здесь - http://www.jaykimble.net/metro-nuggets-async-is-your-friend.aspx

+0

Привет DevTheo, когда у меня есть этот код 'private async Task DoAsyncLoad() // обратите внимание на использование ключевого слова« async » { }' Я получаю ошибки, говорящие о том, что ожидается двоеточие, и мне не хватает директивы using? –

+0

Ключом к тому, что я говорил, является то, что вам необходимо установить Async CTP3 (успешно), а затем включить AsyncCtpLibrary_Phone.dll (входит в состав CTP). Я подозреваю, что вы этого не сделали, поэтому инструменты/компиляторы телефона не знают, что такое «асинхронные» и «ждущие». Я продолжу и обновляю свой пример кода, чтобы добавить немного больше вашего кода, чтобы вы могли видеть, как он выглядит. У меня есть сообщение в блоге в моем блоге на Async - http://www.jaykimble.net/metro-nuggets-async-is-your-friend.aspx (не уверен, что это поможет или нет) – DevTheo

1

Я бы не попытаться сделать их один за другим, как это. «Укладывая» их друг за другом, вы теряете все преимущества асинхронных вызовов в первую очередь. Не только это, но и на мобильной платформе, такой как Windows Phone, вы должны помнить, что сетевые вызовы выстраиваются в очередь для эффективного использования антенны. Когда вы совершаете оба вызова одновременно, у них гораздо больше шансов быть выполненными во время одного и того же соединения с антенной, что является «хорошей вещью».

Затем каждый из ваших обратных вызовов фактически обновляет полностью независимые коллекции. A обновляет коллекцию place, а B обновляет коллекцию top. Так что дело не в том, чтобы эти два шагали друг на друга.

Единственная реальная проблема, которую я вижу здесь, это просто то, что вы обновляете коллекцию top, которая установлена ​​как listBox.ItemsSource. Вам необходимо перевести обновления привязанных данных обратно в поток пользовательского интерфейса (aka Dispatcher thread), чтобы связанные с ними элементы управления обновлялись в правильном потоке.

Таким образом, единственное изменение, которое вы должны сделать, чтобы какой-либо из вашего кода маршала добавления нового элемента к 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); 
     }); 
    } 
} 

При этом вся ваша работа остается асинхронной/CONCURRENT для крохотной части, где вы добавляете товар в top инкассирования.

+0

Класс WebClient уже автоматически вызывает обратный вызов в потоке пользовательского интерфейса, вам не нужно это делать: http://social.msdn.microsoft.com/Forums/en-US/windowsphone7series/thread/594e1422-3b69-4cd2-a09b-fb500d5eb1d8/ – PhilChuang

+0

Mea culpa, если это так, я не эксперт WP. Я привык к «чистым» .NET, где это не так, как работает WebClient. Если это так, то я не могу представить, в чем проблема, потому что я не вижу ничего плохого в этом коде. Думаю, нам понадобится исключение/stacktrace из @M_K. –

0

Даже с лямбдой существует гораздо более элегантное решение - используйте специальное действие, где 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 
}); 
Смежные вопросы