0

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

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

我正在尝试使用异步和等待来多任务处理这个过程。当我单击该按钮时,它工作正常,以大约 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();
    }
4

2 回答 2

1

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

private async Task getItemView()

然后,不要像这样在 click 事件处理程序中Task.Run简单地使用这个调用:await

await getItemView();
于 2016-08-20T01:48:09.103 回答
0

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

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

WebResponse response = await request.GetResponseAsync();
{do stuff}
response.Close();
于 2016-08-22T18:01:14.520 回答