Warning: file_get_contents(/data/phpspider/zhask/data//catemap/2/csharp/314.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# 在Foreach中创建和启动任务_C#_Closures_Task - Fatal编程技术网

C# 在Foreach中创建和启动任务

C# 在Foreach中创建和启动任务,c#,closures,task,C#,Closures,Task,我试图从一个网站上搜集一些数据。这是我的班级: class ClosureCraziness { public string SaveFolder { get; set; } public void Save(Dictionary<string, string> idToWebLocation) { var tasks = new List<Task>(); foreach (var kvp in idToWebL

我试图从一个网站上搜集一些数据。这是我的班级:

class ClosureCraziness
{
    public string SaveFolder { get; set; }

    public void Save(Dictionary<string, string> idToWebLocation)
    {
        var tasks = new List<Task>();
        foreach (var kvp in idToWebLocation)
        {
            var task = new Task(() => Download(kvp.Key, kvp.Value));
            task.Start();
            tasks.Add(task);
        }

        Task.WaitAll(tasks.ToArray());
    }

    void Download(string id, string location)
    {
        var filename = $"{id}.html";
        string source = string.Empty;
        try
        {
            source = GetSource(location);
        }
        catch (Exception e)
        {
            // handle exception
        }

        var path = Path.Combine(SaveFolder, filename);
        using (var sw = new StreamWriter(path))
            sw.Write(source);
    }

    string GetSource(string location)
    {
        using (var client = new WebClient())
        {
            return client.DownloadString(location);
        }
    }
}
为了安全起见:

public void Save(Dictionary<string, string> idToWebLocation)
    {
        var tasks = new List<Task>();
        foreach (var kvp in idToWebLocation)
        {
            var innerKvp = kvp;
            var id = innerKvp.Key;
            var loc = innerKvp.Value;
            var task = new Task(() => Download(id, loc));
            task.Start();
            tasks.Add(task);
        }

        Task.WaitAll(tasks.ToArray());
    }
public void保存(字典idToWebLocation)
{
var tasks=新列表();
foreach(idToWebLocation中的var kvp)
{
var innervp=kvp;
var id=innerKvp.Key;
var loc=内部KVP.值;
var task=新任务(()=>下载(id,loc));
task.Start();
任务。添加(任务);
}
Task.WaitAll(tasks.ToArray());
}
还有,因为谁知道呢:

public void Save(Dictionary<string, string> idToWebLocation)
{
    var tasks = new List<Task>();
    foreach (var kvp in idToWebLocation)
    {
        var innerKvp = kvp;
        var task = new Task(() =>
        {
            var id = innerKvp.Key;
            var loc = innerKvp.Value;
            Download(id, loc);
        });

        task.Start();
        tasks.Add(task);
    }

    Task.WaitAll(tasks.ToArray());
}
public void保存(字典idToWebLocation)
{
var tasks=新列表();
foreach(idToWebLocation中的var kvp)
{
var innervp=kvp;
变量任务=新任务(()=>
{
var id=innerKvp.Key;
var loc=内部KVP.值;
下载(id,loc);
});
task.Start();
任务。添加(任务);
}
Task.WaitAll(tasks.ToArray());
}
但这两种方法都不奏效。显然,我对这段代码是如何编译的缺乏理解,但我的意思是,到底发生了什么

它似乎介于
var filename=$“{id}.html”之间
source=GetSource(位置)位置正在更改。我很确定代码是线程安全的,没有共享状态,对吗

但显然不是,因为当我同步遍历字典时,一切都完全按照预期进行


也许我在这里遗漏了一些基本点,关于外壳、线程、内存等等。我不知道,但我的书桌上满是头发,我快要秃顶了

我认为在创建任务之前,应该尝试创建key和value的局部变量

var key = kvp.Key;
var value = kvp.Value;
var task = new Task(() => Download(key, value));

任务并行库为每种方法都提供了一个功能,非常适合您所做的事情。你可能会发现这与你目前正在尝试做的事情有关:


如果您将代码更改为使用async/await而不是自己构建任务,您是否仍然会遇到问题

class ClosureCraziness
{
    public string SaveFolder { get; set; }

    public void Save(Dictionary<string, string> idToWebLocation)
    {
        var tasks = new List<Task>();
        foreach (var kvp in idToWebLocation)
        {
            tasks.Add(Download(kvp.Key, kvp.Value));
        }

        Task.WaitAll(tasks.ToArray());
    }

    async Task Download(string id, string location)
    {
        var filename = $"{id}.html";
        string source = string.Empty;
        try
        {
            source = await GetSource(location);
        }
        catch (Exception e)
        {
            filename = "e-" + filename;
            var ex = e;
            while (ex != null)
            {
                source += ex.Message;
                source += Environment.NewLine;
                source += Environment.NewLine;
                source += ex.StackTrace;
                ex = ex.InnerException;
            }
        }

        var path = Path.Combine(SaveFolder, filename);
        using (var sw = new StreamWriter(path))
            await sw.WriteAsync(source);
    }

    async Task<String> GetSource(string location)
    {
        using (var client = new WebClient())
        {
            return await client.DownloadStringTaskAsync(location);
        }
    }
}
类封闭性
{
公共字符串保存文件夹{get;set;}
公共作废保存(字典idToWebLocation)
{
var tasks=新列表();
foreach(idToWebLocation中的var kvp)
{
添加(下载(kvp.Key,kvp.Value));
}
Task.WaitAll(tasks.ToArray());
}
异步任务下载(字符串id、字符串位置)
{
var filename=$“{id}.html”;
string source=string.Empty;
尝试
{
source=等待获取源(位置);
}
捕获(例外e)
{
filename=“e-”+文件名;
var ex=e;
while(ex!=null)
{
来源+=例如消息;
source+=Environment.NewLine;
source+=Environment.NewLine;
source+=ex.StackTrace;
ex=ex.InnerException;
}
}
var path=path.Combine(保存文件夹,文件名);
使用(var sw=新StreamWriter(路径))
wait sw.WriteAsync(源);
}
异步任务GetSource(字符串位置)
{
使用(var client=new WebClient())
{
返回wait client.downloadstringtasksync(位置);
}
}
}
与您原来的任务相比,我所做的唯一更改是使用
任务
返回
Write
DownloadString
的版本,将您的助手方法更改为返回
任务
s,并添加一些
异步
s和
等待
s以使代码编译。我手头没有编译器,但这应该非常接近正确


实际上,我并不认为您的原始版本存在问题,但通过将任务创建封装在一个以其输入为参数的函数中,我们应该能够最大限度地减少与闭包相关的bug潜入的可能性。

如果您怀疑字典存在线程问题(我认为也不是,它不参与并行执行),试试ConcurrentDictionary。好好看看源文件,它们真的有你期望的内容吗?@MartinMaat它们没有我期望的内容,这就是问题所在。除非你是说当我运行同步时,在这种情况下,是的,我仔细检查了它们。而且-我无法想象字典是怎么回事。这是C#6($“您可以在这里看到使用的字符串{interpolation}”-这是惊人的)根据.NET 4.5.2编译。但我真的认为这不再是一个闭包问题。也许。我不知道。也许您应该试试老式的
string.Format()
,看看这是否有区别。我猜
$“{key}”
格式对于同样的东西来说是一种语法糖果,但检查也无妨。很抱歉,我忘了你提到它是同步工作的。尝试在下载方法中对代码设置访问锁。如果它有效,请通过减少锁定来缩小它的范围。除非我遗漏了什么,否则字典只在一个线程上使用。你知道吗w什么?你是对的。也就是说,假设你没有在Save()工作流之外的另一个线程中修改该字典(修改后的闭包是错误的)。我将更改我的答案以删除并发字典,但你可能希望检查每个分区工作流的TPL。它是为此类问题而设计的。
class ClosureCraziness
{
    public string SaveFolder { get; set; }

    public void Save(Dictionary<string, string> idToWebLocation)
    {
        var tasks = new List<Task>();
        foreach (var kvp in idToWebLocation)
        {
            tasks.Add(Download(kvp.Key, kvp.Value));
        }

        Task.WaitAll(tasks.ToArray());
    }

    async Task Download(string id, string location)
    {
        var filename = $"{id}.html";
        string source = string.Empty;
        try
        {
            source = await GetSource(location);
        }
        catch (Exception e)
        {
            filename = "e-" + filename;
            var ex = e;
            while (ex != null)
            {
                source += ex.Message;
                source += Environment.NewLine;
                source += Environment.NewLine;
                source += ex.StackTrace;
                ex = ex.InnerException;
            }
        }

        var path = Path.Combine(SaveFolder, filename);
        using (var sw = new StreamWriter(path))
            await sw.WriteAsync(source);
    }

    async Task<String> GetSource(string location)
    {
        using (var client = new WebClient())
        {
            return await client.DownloadStringTaskAsync(location);
        }
    }
}