【问题标题】:APM pattern, Await AsyncAPM 模式,等待异步
【发布时间】:2014-02-17 12:22:29
【问题描述】:

我需要有关如何使用 APM 模式的帮助,我现在正在阅读一些文章,但恐怕我没有太多时间。我真正想要的是获取所有人员(来自数据库的数据)然后获取照片并将其放在自动完成框上 代码:

void listanomesautocomplete(object sender, ServicosLinkedIN.queryCompletedEventArgs e)
        {
            if (e.Result[0] != "")
            {
                for (int i = 0; i < e.Result.Count(); i = i + 3)
                {
                    Pessoa pessoa = new Pessoa();
                    pessoa.Nome = e.Result[i];
                    pessoa.Id = Convert.ToInt32(e.Result[i + 1]);
                    if (e.Result[i + 2] == "")
                        pessoa.Imagem = new BitmapImage(new Uri("Assets/default_perfil.png", UriKind.Relative));
                    else
                    {
                        ServicosLinkedIN.ServicosClient buscaimg = new ServicosLinkedIN.ServicosClient();
                        buscaimg.dlFotoAsync(e.Result[i + 2]);
                        buscaimg.dlFotoCompleted += buscaimg_dlFotoCompleted;//when this completes it saves into a public bitmapimage and then i save it into pessoa.Imagem
                        //basicly, what happends, the listadlfotosasync only happends after this function
                        //what i want is to wait until it completes and have into the class novamsg
                        pessoa.Imagem = img;//saving the photo from dlFotoAsync           

                    }
                    listaPessoas.Add(pessoa);
                }

                tbA_destinatario.ItemsSource = null;
                tbA_destinatario.ItemsSource = listaPessoas;

                BackgroundWorker listapessoas = new BackgroundWorker();
                listapessoas.DoWork += listapessoas_DoWork;
                listapessoas.RunWorkerAsync();
                tb_lerdestmsg.Text = "";
            }
            else
            {
                tbA_destinatario.ItemsSource = null;
                tb_lerdestmsg.Text = "Não encontrado";
            }
        }

【问题讨论】:

标签: c# asynchronous task-parallel-library task async-await


【解决方案1】:

这里有几件事要解决:

  • listanomesautocomplete 事件处理程序应为 async。处理并报告所有可能在其中抛出的异常(通常,此规则适用于任何事件处理程序,但对于 async 事件处理程序来说更重要,因为处理程序内的异步操作继续超出触发事件)。

  • 首先注册dlFotoCompleted 事件处理程序,然后调用dlFotoAsync。不要假设dlFotoAsync总是异步执行。

  • 考虑一下当另一个listanomesautocomplete 被触发而前一个操作仍处于挂起状态时的情况。由于用户的行为,这可能会发生。您可能想要取消并重新启动挂起的操作(如this),或者只是排队等待待处理的操作完成后立即执行(如this)。

回到问题,您需要将EAP pattern 包装为Task,而不是APM。为此使用TaskCompletionSource。您的代码的相关部分可能如下所示:

async void listanomesautocomplete(object sender, ServicosLinkedIN.queryCompletedEventArgs e)
{
    if (e.Result[0] != "")
    {
        for (int i = 0; i < e.Result.Count(); i = i + 3)
        {
            Pessoa pessoa = new Pessoa();
            pessoa.Nome = e.Result[i];
            pessoa.Id = Convert.ToInt32(e.Result[i + 1]);
            if (e.Result[i + 2] == "")
                pessoa.Imagem = new BitmapImage(new Uri("Assets/default_perfil.png", UriKind.Relative));
            else
            {
                // you probably want to create the service proxy 
                // outside the for loop
                using (ServicosLinkedIN.ServicosClient buscaimg = new ServicosLinkedIN.ServicosClient())
                {
                    FotoCompletedEventHandler handler = null;
                    var tcs = new TaskCompletionSource<Image>();

                    handler = (sHandler, eHandler) =>
                    {
                        try
                        {
                            // you can move the code from buscaimg_dlFotoCompleted here,
                            // rather than calling buscaimg_dlFotoCompleted
                            buscaimg_dlFotoCompleted(sHandler, eHandler);

                            tcs.TrySetResult(eHandler.Result);
                        }
                        catch (Exception ex)
                        {
                            tcs.TrySetException(ex);
                        }
                    };

                    try
                    {
                        buscaimg.dlFotoCompleted += handler;
                        buscaimg.dlFotoAsync(e.Result[i + 2]);

                        // saving the photo from dlFotoAsync
                        pessoa.Imagem = await tcs.Task;
                    }
                    finally
                    {
                        buscaimg.dlFotoCompleted -= handler;
                    }
                }
            }
            listaPessoas.Add(pessoa);
        }

        tbA_destinatario.ItemsSource = null;
        tbA_destinatario.ItemsSource = listaPessoas;

        BackgroundWorker listapessoas = new BackgroundWorker();
        listapessoas.DoWork += listapessoas_DoWork;
        listapessoas.RunWorkerAsync();
        tb_lerdestmsg.Text = "";
    }
    else
    {
        tbA_destinatario.ItemsSource = null;
        tb_lerdestmsg.Text = "Não encontrado";
    }
}

【讨论】:

    【解决方案2】:
    void listanomesautocomplete(object sender, ServicosLinkedIN.queryCompletedEventArgs e)
            {
    
                if (e.Result[0] != "")
                {
    
                    List<Pessoa> listaPessoas = new List<Pessoa>();
    
                    for (int i = 0; i < e.Result.Count(); i = i + 3)
                    {
                        Pessoa pessoa = new Pessoa();
                        pessoa.Nome = e.Result[i];
                        pessoa.Id = Convert.ToInt32(e.Result[i + 1]);
                        if (e.Result[i + 2] == "")
                            pessoa.Imagem = new BitmapImage(new Uri("Assets/default_perfil.png", UriKind.Relative));
                        else
                        {
                            ServicosLinkedIN.ServicosClient buscaimg = new ServicosLinkedIN.ServicosClient();
                            buscaimg.dlFotoAsync(e.Result[i + 2]);
                            //THIS ACTUALLY WORKS!!!
                            //THE THREAD WAITS FOR IT!
                            buscaimg.dlFotoCompleted += (s, a) =>
                            {
                                pessoa.Imagem = ConvertToBitmapImage(a.Result);
                            };
                        }
                        listaPessoas.Add(pessoa);
                    }
    
                    if (tbA_destinatario.ItemsSource == null)
                    {
    
                        tbA_destinatario.ItemsSource = listaPessoas;
                    }
                    tb_lerdestmsg.Text = "";
                }
                else
                {
                    tbA_destinatario.ItemsSource = null;
                    tb_lerdestmsg.Text = "Não encontrado";
                }
            }
    

    伙计,我什至没有生气,我很惊讶。 Noseratio,你的回答给了我一个想法,它确实有效! 非常感谢你,我感激不尽;)

    【讨论】:

    • 没问题,但这段代码仍然不正确。想一想如果在您调用 dlFotoAsync 后 1 秒内触发 dlFotoCompleted 会发生什么。到那时,整个for 循环可能会结束。
    • 我认为不可能,我测试的方式,只有 1 个线程完成了所有工作,但我会继续强调程序,再次感谢您的时间!
    • 出了点问题...我完全复制了其他页面上的代码,但它不起作用。我观察了调试工作,它非常神奇,真的,if (tbA_destinatario.ItemsSource == null) { tbA_destinatario.ItemsSource = listaPessoas;如果您检查上面的代码,它没有任何意义吗?在 itemsource 的第一次更新之后,它不应该再工作了......但它确实我已经通过 'tbA_destinatario.ItemsSource' 检查了整个解决方案并且没有出现任何东西,有些东西正在填充 tbA_destinatario.ItemsSource,我想知道是什么。
    • 你真的应该使用TaskCompletionSource 来等待事件,类似于我在回答中的做法。我忍不住要调试这个。
    • 哦,我又准确地复制了一遍,它成功了……在我交付这个项目后(大约 5 小时),我会检查你提供的那些链接,而不是做丑陋的变通方法
    猜你喜欢
    • 2016-12-07
    • 1970-01-01
    • 1970-01-01
    • 2019-02-16
    • 2016-04-26
    • 1970-01-01
    • 2023-03-12
    • 2019-12-12
    • 1970-01-01
    相关资源
    最近更新 更多