2017-03-17 56 views
0

我有收到一个时期的web服务(ExternalWebService)(开始结束日期)并返回所有日志此期间,我想为这项服务经过长时间的电话。问题在于,此服务只允许每个请求发送少量数据,而长时间意味着大量数据,这是导致错误的原因。因此,我决定循环遍历作为参数传递的期间的几个月,并使用任务并行调用此服务,并在任务执行结束时连接结果。下面是代码:如何运行使用相同外部变量的任务?

public List<object> GetList(DateTime start, DateTime end) 
{ 
    List<object> finalList = new List<object>(); 
    object lockList = new object(); 

    DateTime current = start; 

    List<Task> threads = new List<Task>(); 

    do 
    { 
     current = new DateTime(Math.Min(current.AddMonths(1).Ticks, end.Ticks)); 

     Task thread = Task.Run(() => { 
      List<object> partialList = ExternalWebService.GetListByPeriod(from: start, to: current); 
      lock (lockList) 
      { 
       finalList = finalList.Concat(partialList).ToList(); 
      } 
     }); 

     threads.Add(thread); 

     start = current; 
    } 
    while (current < end); 

    Task.WhenAll(threads).Wait(); 

    return finalList; 
} 

此代码的工作,但有一个意想不到的结果,因为变量线程内使用前开始当前变化。那么,我能做些什么来保证开始目前内部使用的日期Task.Run具有相同的值,它们在创建线程时具有相同的值?

+0

你的主要问题在这里是你设置'开始= current'而不等待'Task'你剥离出来,即不断变化的日期任务之前可以使用它们。 – JSteward

+0

没错,但我不能等待任务,因为我需要开始下一个任务并继续循环。我只会等待最后的任务。 – Marcello

+0

我跟着你,希望我的回答如下,让你朝着正确的方向前进。没有必要改变这些日期,你可以保留你需要的日期作为当地人。此外,答案中的方法也消除了不必要的锁定,因为它不会共享和变更列表。 – JSteward

回答

0

您可以创建接收你想要的DateTime的方法,并返回委托它传递给Task.Run方法,是这样的:

private Action GetMethodAction(DateTime current) 
{ 
    return() => { /* your code here */ } 
} 

这样的current价值必将给您的行动返回。希望能帮助到你。

+0

它工作得很好!我会发布完整的答案。 我想知道为什么使用一个方法的变量传递的价值,而内联方法使用参考? – Marcello

3

最好不要共享和改变你的日期。您可以启动一组异步查询Web服务的任务,并将结果展平。

public class GetData { 

    public async Task<IEnumerable<object>> GetDataAsync(DateTime startDate, DateTime endDate) { 
     var daysPerChunk = 28; 
     var totalChunks = (int)Math.Ceiling((endDate - startDate).TotalDays/daysPerChunk); 
     var chunks = Enumerable.Range(0, totalChunks); 

     var dataTasks = chunks.Select(chunkIndex => { 
      var start = startDate.AddDays(chunkIndex * daysPerChunk); 
      var end = new DateTime(Math.Min(start.AddDays(daysPerChunk).Ticks, endDate.Ticks)); 
      return ExternalWebService.GetListByPeriodAsync(from: start, to: end); 
     }); 
     var results = await Task.WhenAll(dataTasks); 

     var data = results.SelectMany(_ => _); 
     return data.ToList(); 
    } 
} 

public class ExternalWebService { 

    private static HttpClient Client { 
     get; 
    } = new HttpClient(); 


    public async static Task<IEnumerable<object>> GetListByPeriodAsync(DateTime from, DateTime to) { 
     var response = await Client.GetAsync("GetListByPeriodFromToUri"); 
     if (response != null && response.IsSuccessStatusCode) { 
      using (var stream = await response.Content.ReadAsStreamAsync()) { 
       using (var reader = new StreamReader(stream)) { 
        var str = reader.ReadToEnd(); 
        return JsonConvert.DeserializeObject<IEnumerable<object>>(str); 
       } 
      } 
     } 
     return Enumerable.Empty<object>(); 
    } 
} 
+0

这可以工作,但我不能访问ExternalWebService。我只是从另一家公司使用这项服务。 – Marcello

+0

@Marcello你打电话给他们的网络服务与网址?如果是这样,你可以打电话异步。 – JSteward

+0

@Marcello Alos,请注意,这不需要做异步。这个方法可以很容易地变成一个'AsParallel'' SelectMany'。但是,如果你正在进行线路,最好是进行异步。 – JSteward

0

这里是为我工作的代码:

protected override List<object> GetList(DateTime start, DateTime end) 
{ 
    List<object> list = new List<object>(); 
    object lockList = new object(); 

    DateTime current = start; 

    List<Task> threads = new List<Task>(); 

    do 
    { 
     current = new DateTime(Math.Min(current.AddMonths(1).Ticks, end.Ticks)); 

     Task thread = Task.Run(GetMethodFunc(start, current)).ContinueWith((result) => 
     { 
      lock (lockList) 
      { 
       list = list.Concat(result.Result).ToList(); 
      } 
     }); 

     threads.Add(thread); 

     start = current; 
    } 
    while (current < end); 

    Task.WhenAll(threads).Wait(); 

    return list; 
} 

private Func<List<object>> GetMethodFunc(DateTime start, DateTime end) 
{ 
    return() => { 
     List<object> partialList = ExternalWebService.GetListByPeriod(from: start, to: end); 
     return partialList; 
    }; 
} 
相关问题