Warning: file_get_contents(/data/phpspider/zhask/data//catemap/2/csharp/273.json): failed to open stream: No such file or directory in /data/phpspider/zhask/libs/function.php on line 167

Warning: Invalid argument supplied for foreach() in /data/phpspider/zhask/libs/tag.function.php on line 1116

Notice: Undefined index: in /data/phpspider/zhask/libs/function.php on line 180

Warning: array_chunk() expects parameter 1 to be array, null given in /data/phpspider/zhask/libs/function.php on line 181

Warning: file_get_contents(/data/phpspider/zhask/data//catemap/2/.net/22.json): failed to open stream: No such file or directory in /data/phpspider/zhask/libs/function.php on line 167

Warning: Invalid argument supplied for foreach() in /data/phpspider/zhask/libs/tag.function.php on line 1116

Notice: Undefined index: in /data/phpspider/zhask/libs/function.php on line 180

Warning: array_chunk() expects parameter 1 to be array, null given in /data/phpspider/zhask/libs/function.php on line 181
C# 如何运行使用相同外部变量的任务?_C#_.net_Multithreading_Task Parallel Library - Fatal编程技术网

C# 如何运行使用相同外部变量的任务?

C# 如何运行使用相同外部变量的任务?,c#,.net,multithreading,task-parallel-library,C#,.net,Multithreading,Task Parallel Library,我有一个web服务(ExternalWebService),它接收一个时间段(开始和结束日期),并返回该时间段的所有日志,我想为该服务调用一个较长的时间段。问题在于,此服务只允许每个请求发送少量数据,而长周期意味着大量数据,这会导致错误。因此,我决定在作为参数传递的时间段中循环几个月,并使用任务并行调用此服务,在任务执行结束时连接结果。代码如下: public List<object> GetList(DateTime start, DateTime end) { List&

我有一个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;
}
公共列表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
。但是如果你要上网的话,最好是异步的。好吧,我发布了一个答案,这个答案对我来说很有效,没有对代码做太多修改。我现在要试试这个代码,看看它对我的案子是否有效。非常感谢。