C# 如何运行使用相同外部变量的任务?
我有一个web服务(ExternalWebService),它接收一个时间段(开始和结束日期),并返回该时间段的所有日志,我想为该服务调用一个较长的时间段。问题在于,此服务只允许每个请求发送少量数据,而长周期意味着大量数据,这会导致错误。因此,我决定在作为参数传递的时间段中循环几个月,并使用任务并行调用此服务,在任务执行结束时连接结果。代码如下:C# 如何运行使用相同外部变量的任务?,c#,.net,multithreading,task-parallel-library,C#,.net,Multithreading,Task Parallel Library,我有一个web服务(ExternalWebService),它接收一个时间段(开始和结束日期),并返回该时间段的所有日志,我想为该服务调用一个较长的时间段。问题在于,此服务只允许每个请求发送少量数据,而长周期意味着大量数据,这会导致错误。因此,我决定在作为参数传递的时间段中循环几个月,并使用任务并行调用此服务,在任务执行结束时连接结果。代码如下: public List<object> GetList(DateTime start, DateTime end) { List&
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;
}
公共列表GetList(日期时间开始,日期时间结束)
{
List finalList=新列表();
对象锁列表=新对象();
日期时间当前=开始;
列表线程=新列表();
做
{
当前=新的日期时间(Math.Min(current.AddMonths(1.Ticks,end.Ticks));
任务线程=任务。运行(()=>{
List partialList=ExternalWebService.GetListByPeriod(从:开始,到:当前);
锁(锁列表)
{
finalList=finalList.Concat(partialist.ToList();
}
});
线程。添加(线程);
启动=电流;
}
而(电流<结束);
Task.WhenAll(threads.Wait();
回归终结者;
}
这段代码可以工作,但会产生意外的结果,因为变量start和current在线程内部使用之前会发生更改。那么,如何确保Task.Run中使用的开始日期和当前日期与创建线程时的值相同?您可以创建一个方法,接收所需的
日期时间,并返回委托将其传递给任务。运行方法,类似于:
private Action GetMethodAction(DateTime current)
{
return () => { /* your code here */ }
}
这样,current
的值将绑定到您返回的操作。希望有帮助。最好不要同时分享和改变你的约会。您可以启动一组任务,异步查询您的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>();
}
}
公共类GetData{
公共异步任务GetDataAsync(DateTime startDate、DateTime endDate){
var daysPerChunk=28;
var totalChunks=(int)Math.天花((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));
返回ExternalWebService.GetListByPeriodAsync(从:开始,到:结束);
});
var结果=等待任务.WhenAll(数据任务);
var data=results.SelectMany(\u=>\ ux);
返回data.ToList();
}
}
公共类外部Web服务{
私有静态HttpClient客户端{
得到;
}=新的HttpClient();
公共异步静态任务GetListByPeriodAsync(日期时间从,日期时间到){
var response=await Client.GetAsync(“GetListByPeriodFromToUri”);
if(response!=null&&response.issucessStatusCode){
使用(var stream=await response.Content.ReadAsStreamAsync()){
使用(变量读取器=新的流读取器(流)){
var str=reader.ReadToEnd();
返回JsonConvert.DeserializeObject(str);
}
}
}
返回可枚举的.Empty();
}
}
以下是对我有用的代码:
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;
};
}
受保护的覆盖列表GetList(日期时间开始,日期时间结束)
{
列表=新列表();
对象锁列表=新对象();
日期时间当前=开始;
列表线程=新列表();
做
{
当前=新的日期时间(Math.Min(current.AddMonths(1.Ticks,end.Ticks));
任务线程=Task.Run(GetMethodFunc(开始,当前)).ContinueWith((结果)=>
{
锁(锁列表)
{
list=list.Concat(result.result.ToList();
}
});
线程。添加(线程);
启动=电流;
}
而(电流<结束);
Task.WhenAll(threads.Wait();
退货清单;
}
私有函数GetMethodFunc(日期时间开始,日期时间结束)
{
return()=>{
List partialList=ExternalWebService.GetListByPeriod(从:开始,到:结束);
回归党派主义;
};
}
它工作得很好!我将发布完整的答案。我想知道为什么使用方法时变量是按值传递的,而内联方法使用引用?您这里的主要问题是,您的设置start=current
,而不等待您剥离的任务,即在任务可以使用它们之前更改日期。确切地说,但是我不能等待任务,因为我需要开始下一个任务并继续循环。我只会等待最后的任务。我在关注你,希望我下面的答案能让你找到正确的方向。没有必要改变这些日期,你可以保留你作为当地人需要的日期。此外,答案中的方法消除了不必要的锁,因为它也不会共享和修改列表。这可能会起作用,但我没有访问ExternalWebService的权限。我正在使用另一家公司的这项服务。@Marcello您是在用url调用他们的web服务吗?如果是这样,您可以使调用异步。@Marcello Alos,请注意,这不需要异步完成。这种方法也可以很容易地变成一个aspallel
SelectMany
。但是如果你要上网的话,最好是异步的。好吧,我发布了一个答案,这个答案对我来说很有效,没有对代码做太多修改。我现在要试试这个代码,看看它对我的案子是否有效。非常感谢。