【问题标题】:C# Async Multiple Web Requests to DataTable - Work appears to stop for unknown reasonsC# 对 DataTable 的多个 Web 请求异步 - 工作似乎因未知原因停止
【发布时间】:2016-08-23 02:01:14
【问题描述】:

我正在开发一个单一表单应用程序,在按下按钮时,它会进行简单的库存数据库 API 查询,并检查每个返回的 ItemID# 是否存在对应的图像,该图像可能存在或不存在于由 ID# 形成的 URL 中。我目前正在通过为每个 URL 发送 HttpWebRequest.Method = "HEAD" 请求来执行此操作,除非触发了 catch 块,否则返回 true。

数据库查询可能返回 50 - 150 个零件号,并且以这种方式分别向每个零件发送 HEAD 请求大约需要 5 分钟,而且效率不高。

我正在尝试使用 async 和 await 对这个过程进行多任务处理。当我单击该按钮时,它工作正常,以大约 2/秒的速度将行一一异步地加载到我的 DataGridView 中(这还不错,但如果可能的话,我仍然想加快速度)。

但是:在找到 2 个成功的 URL 响应后,它停止加载行并且似乎只是放弃了,原因我不知道???并且重新启用 UI 的 syncContext 块永远不会执行,因为工作永远不会完成。任何人都可以看到可能导致这种情况发生的原因吗?

根据这个文档,我一直在松散地工作:

“如何:使用 async 和 await (C#) 并行发出多个 Web 请求” https://msdn.microsoft.com/en-us/library/mt674880.aspx

namespace ImageTableTest
{
public partial class ImageTableTestForm : Form
{
    //P21 Authentication Variables
    private static Token P21token = null;
    private static RestClientSecurity rcs;

    //Create Tables and bindingSource
    DataTable itemDataIMG = new DataTable();
    DataTable itemDataNOIMG = new DataTable();
    DataTable itemDataComplete = new DataTable();
    BindingSource bindingSource = new BindingSource();

    private readonly SynchronizationContext synchronizationContext;


    public ImageTableTestForm()
    {
        InitializeComponent();

        //Create syncContexct on UI thread for updating UI
        synchronizationContext = SynchronizationContext.Current;

        //authenticate database API function
        authenticateP21();     

        //Designing DataTables
        itemDataIMG.Columns.Add("MPN#", typeof(string));
        itemDataIMG.Columns.Add("IMG", typeof(bool));
        itemDataIMG.Columns[1].ReadOnly = true;

        itemDataNOIMG.Columns.Add("MPN#", typeof(string));
        itemDataNOIMG.Columns.Add("IMG", typeof(bool));
        itemDataNOIMG.Columns[1].ReadOnly = true;

        itemDataComplete.Columns.Add("MPN#", typeof(string));
        itemDataComplete.Columns.Add("IMG", typeof(bool));
        itemDataComplete.Columns[1].ReadOnly = true; 

        //bind to DataGridView itemView
        bindingSource.DataSource = itemDataComplete;          
        itemView.DataSource = bindingSource;
        itemView.AutoGenerateColumns = false;
    }



    private async void testBtn_Click(object sender, EventArgs e)
    {
        //When button is clicked, disable UI and
        //start background work:
        testBtn.Enabled = false;
        loadSpinner.Visible = true;

        await Task.Run(() =>
        {
            getItemView();
        });
    }


    private async void getItemView()
    {
        try
        {
            //This executes the query and returns an array of Part objects:
            PartResourceClient prc = new PartResourceClient(ConfigurationManager.AppSettings["P21.BaseURI"], rcs);
            prc.QueryFilter("add_to_ebay eq 'Y'");
            Part[] pResults = prc.Resource.GetParts();              

            int numParts = pResults.Length;                
            Task<bool>[] taskArray = new Task<bool>[numParts];
            bool[] IMGboolArray = new bool[numParts];

            //For each part, create CheckImageURL task and add to task Array
            //Then Await execution
            for (int i = 0; i < numParts; i++)
            {
                taskArray[i] = CheckImageURL(pResults[i].ItemId);
                IMGboolArray[i] = await taskArray[i];
            }                
        }
        catch (Exception e)
        {
            MessageBox.Show(e.ToString());
        }

        //When all Tasks finish, remove loadSpinner, re-enable UI
        //(This never executes for unknown reasons.)
        synchronizationContext.Post(new SendOrPostCallback(o =>
        {              
            loadSpinner.Visible = false;
            testBtn.Enabled = true;
        }), null);

        MessageBox.Show("<DONE>");
    }


    async Task<bool> CheckImageURL(string MPN)
    {
        //Here I am forming and executing the web HEAD request,
        //If there is there is a 'NOT FOUND' response it goes to 'catch' block:
        string URL = "https://s3-us-west-2.amazonaws.com/www.crosscreektractor.com/ebay-images/" + MPN + "_e.png";
        HttpWebRequest request = (HttpWebRequest)HttpWebRequest.Create(URL);
        request.Method = "HEAD";
        try
        {
            await request.GetResponseAsync();
            synchronizationContext.Post(new SendOrPostCallback(o =>
            {
                addDataRows(MPN, true);
            }), null);

            return true;
        }
        catch
        {
            synchronizationContext.Post(new SendOrPostCallback(o =>
            {
                addDataRows(MPN, false);
            }), null);

            return false;
        }
    }

    private void addDataRows(string MPN, bool IMG)
    {
        //Add data to respective table:
        if (IMG)
        {
            itemDataIMG.Rows.Add(MPN, IMG);
        }
        else
        {
            itemDataNOIMG.Rows.Add(MPN, IMG);
        }

        //Here I am sorting the IMG and NOIMG tables,
        //then merging them into the Complete table which
        //The DataGridView is bound to, so that IMG entries are on top:
        itemDataIMG.DefaultView.Sort = ("MPN# DESC");
        itemDataNOIMG.DefaultView.Sort = ("MPN# DESC");

        itemDataComplete.Clear();
        itemDataComplete.Merge(itemDataIMG);
        itemDataComplete.Merge(itemDataNOIMG);
        itemView.Refresh();
    }

【问题讨论】:

  • 你在 TAP 和 async/await 中错过了。实现和使用基于任务的异步模式是错误的方法。
  • 哈姆雷特,这是导致它停止的原因吗,或者如果以不同的方式处理它会降低开销吗?也许您可以提供文档或告诉我哪些行看起来很乱?
  • google 关键字 TAP、async/await、基于任务的异步模式。
  • 这就是我走到这一步的方式,它似乎工作正常,除了它在找到 2 个成功的 URL 后挂起。我已经阅读了很多关于 TAP、async 和 await 的内容。这是我找到的微软指南中描述的实现。如果您发现我编码错误的内容,请通知我。
  • 在微软指南中不能是这样的await request.GetResponseAsync(); synchronizationContext.Post(。有一个范式await all the way。你应该遵循那个范式。你的getItemView 应该返回Task,你应该在你的处理程序中等待Task

标签: c# .net datagridview async-await system.net.httpwebrequest


【解决方案1】:

getItemView() 方法更改为Task 返回,如下所示:

private async Task getItemView()

然后在 click 事件处理程序中使用 Task.Run 而不是简单地使用 await 这个调用,如下所示:

await getItemView();

【讨论】:

  • CheckImageURL() 由于某种原因两次返回 true 后,它仍然只是停止添加行。
  • 另外,当我这样做时,UI会在查询完成时冻结,所以我想我会继续使用 await Task.Run ()
【解决方案2】:

感谢您对我的 TAP 模式的建议,肯定会学到很多关于 TAP 的知识。

我的问题的解决方案是 HttpWebRequest 连接限制。在成功的请求上,它不会自动关闭连接,您必须通过抓取 WebResponse 并关闭它来做到这一点(仅在成功的连接上需要):

WebResponse response = await request.GetResponseAsync();
{do stuff}
response.Close();

【讨论】:

    猜你喜欢
    • 2023-04-03
    • 1970-01-01
    • 2023-03-29
    • 1970-01-01
    • 2021-12-07
    • 1970-01-01
    • 1970-01-01
    • 2012-12-04
    • 1970-01-01
    相关资源
    最近更新 更多