如何使异步任务同步

本文关键字:同步 任务 异步 何使 | 更新日期: 2023-09-27 18:31:34

我正在尝试从两个不同的网站获取两种类型的数据并将其绑定到一个列表中,但是我在异步时遇到了问题,我想做的是从rss获取信息将其添加到列表中,然后从另一个网站获取信息将其添加到列表中,然后将两者添加到绑定的可观察集合中。 但是下载字符串异步过度运行,应用程序崩溃。你能帮我吗?

我的代码是

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

}

如何使异步任务同步

我不会试图像那样一个接一个地制作它们。通过像这样一个接一个地"堆叠"它们,您首先会失去异步调用的所有优势。不仅如此,在像Windows Phone这样的移动平台上,您必须记住,网络呼叫需要排队才能有效使用天线。当您同时进行两个呼叫时,它们在同一天线连接期间执行的机会要高得多,这是一件"好事"。

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

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

因此,您应该对任何代码进行的唯一更改是将新项添加到 top 集合的封送回B回调中的Dispatcher线程。那看起来像这样:

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

这样,您的所有工作都保持异步/并发,除了将项目添加到top集合的一小部分。

这更像是一个替代答案(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 标记为异步 - 并设置 Task 的返回)。

顺便说一句,您实际上可以加载最新的异步CTP3并发布以这种方式编写的WP代码。不过,大多数人有点害怕CTP。

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

首先,我相信在您的情况下,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;
    }
}

}
即使使用

lambda,也有一个更优雅的解决方案 - 使用自定义操作,其中 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
});