Async期间的线程安全问题,如何解决这个问题

时间:2014-03-30 10:38:47

标签: c# multithreading asynchronous windows-phone-8 dotnet-httpclient

我需要使用相同的HttpClient实例从不同方法执行4个HttpClient请求,我认为这会导致线程安全问题。让我再解释一下。

在我开发的Windows Phone应用程序中,有一个“MainViewModel”'类。此类具有异步方法,以从Web服务器获取数据并处理响应。 Async方法名为' LoadData'。我在这个类中甚至还有两个Async方法(' ScheduleArrayAsync'和#39; CurrActivityAsync')来帮助处理我从服务器获取的数据。

来自' LoadData'方法我做3个HttpClient请求(这部分工作就像魅力),在处理来自所有这三个请求的响应时,我需要调用' ScheduleArrayAsync'方法。从那里我必须创建一个新的HttpClient请求(这是问题所在)。即使我使用try / catch语句,也不会生成最后一个请求,也不会生成错误代码。

是什么让我认为它是一个线程问题,如果我将最后一个HttpClient请求移动到' LoadData'方法,就像测试一样,它再次起作用。

MainViewModel类: -

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类: -

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
    }
}

我理解这个问题吗?如何克服它?因为我真的需要从&#39; ScheduleArrayAsync&#39;制作最后一个HttpClient请求。而不是来自&#39; LoadData&#39;

修改 只是想提一下,在我努力解决问题的过程中,我在RequestResponse类中的每个方法中都有不同的HttpClient实例。除了我上面提到的,当我从内部调用第四个请求时,LoadData&#39;方法即使它是httpclient的一个实例,一切都按预期工作。

2 个答案:

答案 0 :(得分:2)

在某些方法中,您可能会将async void作为签名。使用async void将阻止调用函数捕获抛出异常。异步void方法抛出的异常只能被全局异常处理程序捕获。

答案 1 :(得分:0)

最后我已经修好了,多亏了Darrel。

要解决问题,我需要更改&#39; CurrActivityAsync&#39;方法

private async void CurrActivityAsync(JToken token, string jString)

private void CurrActivityAsync(JToken token, string jString)

由于这种方法没有等待它内部,这就是为什么它不需要签名中的异步。我的坏事我在原帖中没有提到过。

这是我唯一需要做的事情,但我甚至改变了我的&#39; ScheduleArrayAsync&#39; 更准确的是Async。

private async void ScheduleArrayAsync(JToken token, string jString)

private async Task ScheduleArrayAsync(JToken token, string jString)

Darrel 请您写下您的最后一条评论作为答案,以便获得一些学分。