C#.NET中的Task.WaitAll方法在极少数情况下抛出异常

时间:2014-02-03 13:24:11

标签: c# .net multithreading

我正在使用API​​来获取数据并将其存储在数据库中。问题是API每次调用只返回50条记录,因此为了在更短的时间内获得更多数据,我们在应用程序中使用了 Parallel.For 。我在下面使用的代码在大多数情况下成功地将数据保存在数据库中,但在极少数情况下抛出异常。它在极少数情况下抛出的例外是

  

tasks数组包含至少一个null元素。参数名称:任务|在System.Threading.Tasks.Task.WaitAll(Task [] tasks,Int32 millisecondsTimeout,CancellationToken cancellationToken)处于System.Threading.Tasks.Task.WaitAll(Task [] tasks,Int32 millisecondsTimeout)。

我不知道异常的原因,因为大部分时间它都给出了正确的结果,我可以将数据保存在数据库中。下面是我写的代码。请帮我解决这个问题,我是并行编程的新手。

object objobject = new object();

public void GetDataConcurrent()
{
    var tasks = new List<Task>();
    ParallelOptions objParallelOptions = new ParallelOptions();
    CancellationTokenSource objCancellationTokenSource = new CancellationTokenSource();
    objParallelOptions.MaxDegreeOfParallelism = 3;
    objParallelOptions.CancellationToken = objCancellationTokenSource.Token;
    string CompletePostData = "PostData to send to API";
    string tException = string.Empty;
    int noOfrequests = 15;
    string jsonResponse=string.Empty; 
    try
    {
        Parallel.For(0, noOfrequests, objParallelOptions, (ChangeSetValue) =>
        {
            tasks.Add(Task.Factory.StartNew(() =>
            {
                var webRequest = WebRequest.Create("http://urlofAPI");
                webRequest.ContentType = "application/json";
                webRequest.Method = "POST";
                webRequest.Proxy = WebRequest.DefaultWebProxy;
                webRequest.Proxy.Credentials = CredentialCache.DefaultCredentials;
                POSTDATA objPOSTDATA = new POSTDATA();
                objPOSTDATA.CompletePostData = CompletePostData + ",'ChangeSetValue':'" +   (ChangeSetValue) + "'}";
                string json = new      System.Web.Script.Serialization.JavaScriptSerializer().Serialize(objPOSTDATA);
                var datatoSend = Encoding.UTF8.GetBytes(json);
                webRequest.GetRequestStream().Write(datatoSend, 0, datatoSend.Length);
                webRequest.GetReponseAsync().ContinueWith(t => 
                {
                    if (t.Exception == null)
                    {
                        using (var sr = new StreamReader(t.Result.GetResponseStream()))
                        {
                            lock (objobject)
                            {
                                string str = sr.ReadToEnd();
                                jsonResponse = jsonResponse + str; 
                                jsonResponse = jsonResponse.Replace("<string xmlns=\"http://schemas.microsoft.com/2003/10/Serialization/\">", string.Empty);
                                jsonResponse = jsonResponse.Replace("</string>", string.Empty);
                                jsonResponse = jsonResponse.Substring(1, jsonResponse.Length - 2);
                                SaveData();// Method to Save Data in Database by Deserializing the JSON Response
                                jsonResponse = string.Empty;
                            }
                        }
                    }
                    else
                    {
                        tException = t.Exception.InnerException.Message;
                    }
                }).Wait();
            }));
        });
        Task.WaitAll(tasks.ToArray());
    }
    catch (OperationCanceledException ex)
    {
        LogException(InsertLogInformation(ex.Message));
    }
    catch (AggregateException ex)
    {
        for (int j = 0; j < ex.InnerExceptions.Count; j++)
        {
            LogException(InsertLogInformation(ex.Message));
        }
    }
    catch (Exception ex)
    {
        LogException(InsertLogInformation(ex.Message));
    }
}

public void SaveData()
{
    //Method to Save Data in DataBase
}

public static class WebRequestExtensions
{
    public static Task<WebResponse> GetReponseAsync(this WebRequest request)
    {
        return Task.Factory.FromAsync<WebResponse>(request.BeginGetResponse, request.EndGetResponse, null);
    }
}

public class POSTDATA
{
    public string CompletePostData { get; set; }
}

1 个答案:

答案 0 :(得分:3)

要扩展@ ta.speot.is的评论(因为没有人似乎想要添加这个问题的答案)。问题是您要添加到Parallel.For内的tasks数组。 List<T>不是线程安全的,因此您应该使用锁定语句同步对List<T>.Add的调用。

我不确定的另一件事是你在Parallel.For内创建任务的原因。 Parallel.For将自己创建任务,因此您可以完全取消任务列表。