0

동일한 HttpClient 인스턴스를 사용하는 다른 메서드에서 4 개의 HttpClient 요청을 수행해야합니다. 이는 스레드 안전 문제를 일으키는 것으로 생각됩니다. 내가 더 설명해 줄께.비동기 중 스레드 안전 문제,이 문제를 해결하는 방법

Windows Phone 애플리케이션에서 'MainViewModel'클래스가 개발 중입니다. 이 클래스에는 웹 서버에서 데이터를 가져와 응답을 처리하는 비동기 메소드가 있습니다. 비동기 메서드의 이름은 'LoadData'입니다. 나는이 클래스에서 2 개의 비동기 메서드 ('ScheduleArrayAsync'및 'CurrActivityAsync')를 사용하여 서버에서 가져 오는 데이터를 처리하는 데 도움을줍니다.

'LoadData'메서드에서 3 가지 요청의 응답을 처리하는 동안 3 개의 HttpClient 요청 (이 부분은 매력처럼 작동 함)을 처리하는 동안 'ScheduleArrayAsync'메서드를 호출해야합니다. 거기에서 새로운 HttpClient Request (여기에 문제가있다)를 만들어야한다. 이 마지막 요청은 생성되지 않으며 try/catch 문을 사용할 때도 오류 코드가 생성되지 않습니다.

내가 스레드 문제라고 생각하게 만드는 이유는 마지막 HttpClient 요청을 테스트와 마찬가지로 'LoadData'메서드로 옮기면 다시 작동하기 때문입니다.

MainViewModel 클래스 : - :

public class RequestResponse 
{ 
    public string Response { get; private set; } 

    private HttpClient client = new HttpClient(new HttpClientHandler() 
    { 
     UseCookies = true, 
     CookieContainer = CookieHandler.Cookiejar, 
     AllowAutoRedirect = false, 
     AutomaticDecompression = DecompressionMethods.Deflate | DecompressionMethods.GZip 
    }); 

    public async Task<string> GetResponse(Uri baseuri, string uriString) 
    { 
     if (client.BaseAddress == null) 
     { 
      client.BaseAddress = baseuri; 
     } 
     client.DefaultRequestHeaders.Accept.Add(new System.Net.Http.Headers.MediaTypeWithQualityHeaderValue("application/xhtml+xml")); 
     var response = await client.GetAsync(baseuri + uriString); 
     string webresponse = null; 
     if (response.IsSuccessStatusCode) 
     { 
      var resp = await response.Content.ReadAsByteArrayAsync(); 
      var encode = Encoding.GetEncoding("iso-8859-1"); 
      var respString = encode.GetString(resp, 0, resp.Length - 1); 
      webresponse = respString; 
     } 
     return Response = webresponse; 
    } 

    public async Task<string> GetSlotInforPOST(string timeId) 
    { 
     /// If the method is called from inside 'LoadData' it works. 
     /// But if it is called from inside ScheduleArrayAsync, it will break at line marked with //*** 
     try 
     { 
      var baseUri = new Uri("https://uri/"); 
      const string slotInfo = "cgi-bin/slotInfo.pl"; 

      if (client.BaseAddress == null) 
       client.BaseAddress = baseUri; 
      client.DefaultRequestHeaders.Accept.Add(new System.Net.Http.Headers.MediaTypeWithQualityHeaderValue("application/x-www-form-urlencoded")); 
      HttpContent content = new FormUrlEncodedContent(new[] 
      { 
       new KeyValuePair<string,string>("timeId",timeId) 
      }); 
      var response = await client.GetAsync(baseUri + slotInfo); // *** 
      string webresponse; 
      if (response.IsSuccessStatusCode) 
      { 
       var respo = await client.PostAsync(baseUri + slotInfo, content); 
       var resp = await respo.Content.ReadAsByteArrayAsync(); 
       var encode = Encoding.GetEncoding("iso-8859-1"); 
       var respString = encode.GetString(resp, 0, resp.Length - 1); 
       webresponse = respString; 

      } 
     } 
     catch (Exception exp) 
     { 
      var s = exp.Message; 
     } 
     return Response; 
    } 

    public async Task<string> GetVacationSlotInfoPOST(string vacationId, string date) 
    { 
     // Do some HttpClient Request 
    } 
} 

내가 문제를 잘 이해하고 있습니까 - 아래

public class MainViewModel : INotifyPropertyChanged 
{ 
    public MainViewModel() 
    { 
     this.Employees = new ObservableCollection<Employee>(); 
    } 

    public ObservableCollection<Employee> Employees { get; private set; } 

    private JsonTextWriter jsonW; 

    private string Owner; 

    private RequestResponse reqPList; 

    public bool IsDataLoaded 
    { 
     get; 
     private set; 
    } 
    public async void LoadData() 
    { 
     var baseUri = new Uri("https://uri/"); 

     await CookieHandler.GetCookies(baseUri); // A separate request to get some cookies 

     reqPList = new RequestResponse(); // The class that handle the Httpclinet 

     await reqPList.GetResponse(baseUri, pList); // First request 
     XmlConvertor.ConvertToXml(reqPList.Response); 
     var phoneListResponse = XmlConvertor.XmlString; 

     await reqPList.GetResponse(baseUri, currActiv); // Second request 
     XmlConvertor.ConvertToXml(reqPList.Response); 
     var currActivResponse = XmlConvertor.XmlString; 

     await reqPList.GetResponse(baseUri, sched); // Third request 
     XmlConvertor.ConvertToXml(reqPList.Response); 
     var schedResponse = XmlConvertor.XmlString; 

     //await reqPList.GetSlotInforPOST("154215"); 
     var handler = new DataHandler(); 
     await handler.phoneListHandler(phoneListResponse); 
     await handler.CurrActivitiesHandler(currActivResponse); 
     await handler.ScheduleHandler(schedResponse); 
     /// Do some processing included call this line 

        #region Current activity 
        CurrActivityAsync(item, handler.currActivitiesJSON); 
        #endregion 


     this.IsDataLoaded = true; 
    } 

    private async void CurrActivityAsync(JToken token, string jString) 
    { 
     // Some processing 
    } 

    private async void ScheduleArrayAsync(JToken token, string jString) 
    { 
     try 
     { 
      // Do some more processing and call the fourth request 
          if (addedInfo[0].Contains("slotInfo")) 
           await reqPList.GetSlotInforPOST(addedInfo[1]); 
          else if (addedInfo[0].Contains("vacationInfo")) 
           await reqPList.GetVacationSlotInfoPOST(addedInfo[1], addedInfo[2]); 

     } 
     catch (Exception exp) 
     { 

      var d = exp.Message; 
     } 
    } 

    public event PropertyChangedEventHandler PropertyChanged; 
    private void NotifyPropertyChanged(String propertyName) 
    { 
     PropertyChangedEventHandler handler = PropertyChanged; 
     if (null != handler) 
     { 
      handler(this, new PropertyChangedEventArgs(propertyName)); 
     } 
    } 
} 

는 RequestResponse 클래스입니다? 그것을 극복하는 방법? 정말 'LoadData'

편집 에서 'ScheduleArrayAsync'에서 마지막 HttpClient를 요청을하지해야하므로 그냥 내 각 방법의 문제를 해결하기 위해 내 노력하는 동안 나는 HttpClient를의 다른 인스턴스을 한 것으로 언급하고 싶었다 RequestResponse 클래스 앞에서 언급했듯이, 'LoadData'메소드에서 네 번째 요청을 호출하면 모든 것이 httpclient의 한 인스턴스 일 때도 의도 된대로 작동합니다.

+1

같은 HttpClient 인스턴스를 사용합니다. 그리고 당신은 왜 이것을합니까? 너는 어떻게 든 그것에 붙어 있니? 새로운 것을 만들 수 있고 그런 문제는 없습니다. – nvoigt

+1

@nvoigt - 내가 게시 할 편지에 거의 : –

+0

@nvoigt는 현명한 성능이 좋지 않습니까? 나는 이것에 대해 뭔가를 읽었을 것이라고 생각한다. - http://social.msdn.microsoft.com/Forums/en-US/4e12d8e2-e0bf-4654-ac85-3d49b07b50af/best-practice-usage-of-httpclient-for- rest-calls-maximum-throughput? forum = netfxnetcom – DreamNet

답변

2

일부 메소드에서 서명으로 가지고있는 비동기 무효 일 가능성이 있습니다. async void를 사용하면 함수가 예외를 발생시키는 것을 막을 수 없습니다. 비동기 void 메 소드에서 발생 된 예외는 전역 예외 핸들러에 의해서만 트랩 될 수 있습니다.

+0

'async void'를 사용하는 것이 문제의 원인이라는 데 동의하지만, 예외를 삼켜 버린 것이 아닙니다. 꽤 대조적 인 것; 'async void'는 매우 큰 소리로 예외를 처리합니다. –

+0

@StephenCleary 내 수정 버전의 소리가 더 좋습니까? –

+0

네, 그게 더 정확하다고 생각합니다. –

0

마지막으로 Darrel에게 고쳐주었습니다.

문제를 해결하려면 'CurrActivityAsync'메소드의 서명을 변경해야했습니다. 이 방법으로

private void CurrActivityAsync(JToken token, string jString) 

private async void CurrActivityAsync(JToken token, string jString) 

에서

는 서명에 비동기 필요하지 않은 이유는 그것이 내부에는 await를 없었다. 내 나쁜 내가 원래 게시물에 언급하지 않았다.

이것은 내가 할 필요가있는 유일한 것이었지만 심지어는 'ScheduleArrayAsync' 의 서명을 비동기에 맞게 수정했습니다. 당신은 어떤 크레딧을받을 수 있도록 대럴

private async Task ScheduleArrayAsync(JToken token, string jString) 

private async void ScheduleArrayAsync(JToken token, string jString) 

에서

당신은 대답으로 마지막 코멘트를 쓸 수 있습니다하시기 바랍니다.

관련 문제