0

버튼 누르기에서 간단한 인벤토리 데이터베이스 API 쿼리를 만들고, 반환 된 각 ItemID를 확인하는 단일 양식 앱을 개발 중입니다. ID #에서 형성된 URL에 존재할 수도 있고 존재하지 않을 수도있다. 나는 현재 각 URL에 대해 HttpWebRequest.Method = "HEAD"요청을 보내서이를 수행하고 있으며 catch 블록이 트리거되지 않는 한 true를 반환합니다.DataTable에 대한 비동기 다중 웹 요청 - 알 수없는 이유로 작업이 멈춘 것처럼 보입니다.

데이터베이스 쿼리는 50-150 개의 부품 번호를 반환 할 수 있으며 이러한 방식으로 개별적으로 HEAD 요청을 보내는 데 약 5 분이 소요되며 이는 생산적이지 않습니다.

이 프로세스를 async와 await을 사용하여 다중 작업하려고합니다. 버튼을 클릭하면 비동기 적으로 DataGridView에 행을 한 개씩 하나씩로드 할 수 있습니다. 속도는 약 2/초 (나쁘지는 않지만 가능하면이 속도를 높이는 것이 좋습니다)입니다.

두 개의 성공적인 URL 응답을 찾으면 행로드가 중지되고 나에게 알 수없는 이유로 포기하는 것처럼 보입니다. ??? 그리고 작업이 완료되지 않기 때문에 UI를 다시 활성화하는 syncContext 블록은 실행되지 않습니다. 아무도 이것이 일어날 수있는 원인을 볼 수 있습니까?

:

내가 느슨하게 일하고있다이 문서 기반으로 "방법 : 비동기를 사용하여 병렬로 여러 웹 요청을하고 기다리고 있습니다 (C#을)" 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(); 
    } 
+0

당신은 TAP 놓친 및 비동기/await를하고 있습니다 . 작업 기반 비동기 패턴을 구현하고 사용하는 것은 잘못된 접근 방식입니다. –

+0

햄릿 (Hamlet), 햄릿이 멈추는 원인이 무엇입니까? 그렇지 않으면 다르게 수행하면 오버 헤드가 낮아 집니까? 어쩌면 당신은 문서를 제공하거나 어떤 라인이 엉망으로 보이는지 말할 수 있습니까? – mholberger

+0

google by 키워드 TAP, async/await, 작업 기반 비동기 패턴. –

답변

0

감사합니다, 확실히 TAP에 대해 많이 배우고.

내 문제의 해결책은 HttpWebRequest 연결 제한입니다. 성공적인 요청에 그것은 다음 연결을 자동으로 닫히지 않습니다, 당신은 (단지 성공적인 연결에 필요한)를 WebResponse를 잡아 그것을 닫아 그렇게해야합니다

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

변경 getItemView() 방법 Task을 수과 같이, 반환 :

private async Task getItemView() 

을 그리고 대신을 사용하는단순히 await과 같이 클릭 이벤트 처리기에서이 전화 : 내 TAP 패턴에 대한 조언을

await getItemView(); 
+0

CheckImageURL()이 어떤 이유로 2 번 true를 반환 한 후에도 여전히 행을 더 이상 추가하지 않습니다. – mholberger

+0

또한이 방법을 사용하면 쿼리가 완료 될 때 UI가 정지되므로 Task.Run()을 계속 사용하는 것으로 생각됩니다. – mholberger