Warning: file_get_contents(/data/phpspider/zhask/data//catemap/2/csharp/336.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#_Multithreading_Recursion_Tree - Fatal编程技术网

C#使用多线程递归扫描树

C#使用多线程递归扫描树,c#,multithreading,recursion,tree,C#,Multithreading,Recursion,Tree,我正在扫描一些目录中的项目。我刚读过这个问题,但我还是想把它变成多重的。尽管每个人都说驱动器将成为瓶颈,但我有一些观点: 这些驱动器可能大多是“单线程”的,但您如何知道它们将来会带来什么呢 如何知道正在扫描的不同子路径是同一个物理驱动器 我在System.IO上使用了一个抽象层(甚至两个),以便以后在不同的场景中重用代码 因此,我的第一个想法是使用任务,第一个虚拟实现是: public async Task Scan(bool recursive = false) { var t =

我正在扫描一些目录中的项目。我刚读过这个问题,但我还是想把它变成多重的。尽管每个人都说驱动器将成为瓶颈,但我有一些观点:

  • 这些驱动器可能大多是“单线程”的,但您如何知道它们将来会带来什么呢
  • 如何知道正在扫描的不同子路径是同一个物理驱动器
  • 我在
    System.IO
    上使用了一个抽象层(甚至两个),以便以后在不同的场景中重用代码
因此,我的第一个想法是使用任务,第一个虚拟实现是:

public async Task Scan(bool recursive = false) {
    var t = new Task(() => {
        foreach (var p in path.scan) Add(p);
        if (!recursive) return;
        var tks = new Task[subs.Count]; var i = 0;
        foreach (var s in subs) tks[i++] = s.Scan(true);
        Task.WaitAll(tks);
    }); t.Start();
    await t;
}
我不喜欢为每个项目创建一个
任务
,通常这看起来并不理想,但这只是一个测试,因为任务会自动管理线程

这种方法有效,但速度很慢。完成整个程序需要5s,而下面的单一威胁版本大约需要0.5s才能在同一数据集上完成整个程序:

public void Scan2(bool recursive = false) {
    foreach (var p in path.scan) Add(p);
    if (!recursive) return;
    foreach (var s in subs) s.Scan2(true);
}
我不知道拳头法到底出了什么问题。机器没有负载,杯子使用量很小,驱动很好。。。我试着用NProfiler对它进行评测,它没有告诉我太多,除了这个程序一直在执行
Task.WaitAll(tks)

我还编写了一个线程锁定计数机制,在添加每个项时调用该机制。也许是它的问题

#region SubCouting
public Dictionary<Type, int> counters = new Dictionary<Type, int>(); 
private object cLock = new object();
private int _sc = 0;
public int subCount => _sc;
private void inCounter(Type t) {
    lock (cLock) {
        if (!counters.ContainsKey(t)) counters.Add(t, 1);
        counters[t]++;
        _sc++;
    }
    if (parent) parent.inCounter(t);
}
#endregion
#区域子路由
公共字典计数器=新字典();
私有对象时钟=新对象();
私有整数_sc=0;
公共国际子计数=>\u sc;
专用无效输入器(t型){
锁(时钟){
如果(!counters.ContainsKey(t))counters.Add(t,1);
计数器[t]++;
_sc++;
}
如果(父项)父项输入(t);
}
#端区
但是,即使线程在这里等待,执行时间是否会与单线程版本类似,而不是慢10倍


我不知道该怎么做。如果我不想使用任务,我是否需要手动管理线程,或者已经有一些库可以很好地适合这项工作了?

我想你差不多找到了<代码>任务。WaitAll(tks)是问题所在。由于这是一个同步操作,因此可以为此阻止一个线程。您很快就会退出线程,所有线程都在等待一些没有线程可运行的任务。您可以使用async解决这个问题,将等待替换为
wait Task.whalll(…)
。它将在等待时释放线程。对于某些工作负载,多线程版本的速度要快得多。当只是IO绑定时,它大致相等

ConcurrentBag<string> result = new ConcurrentBag<string>();
List<string> result2 = new List<string>();

public async Task Scan(string path)
{
    await Task.Run(async () =>
    {
        var subs = Directory.GetDirectories(path);
        await Task.WhenAll(subs.Select(s => Scan(s)));

        result.Add(Enumerable.Range(0, 1000000).Sum(i => path[i % path.Length]).ToString());
    });
}

public void Scan2(string path)
{
    result2.Add(Enumerable.Range(0, 1000000).Sum(i => path[i % path.Length]).ToString());

    var subs = Directory.GetDirectories(path);
    foreach (var s in subs) Scan2(s);
}

private async void button4_Click(object sender, EventArgs e)
{
    string dir = @"d:\tmp";

    System.Diagnostics.Stopwatch st = new System.Diagnostics.Stopwatch();
    st.Start();
    await Scan(dir);
    st.Stop();
    MessageBox.Show(st.ElapsedMilliseconds.ToString());

    st = new System.Diagnostics.Stopwatch();
    st.Start();
    Scan2(dir);            
    st.Stop();
    MessageBox.Show(st.ElapsedMilliseconds.ToString());

    MessageBox.Show(result.OrderBy(x => x).SequenceEqual(result2.OrderBy(x => x)) ? "OK" : "ERROR");
}
ConcurrentBag结果=新的ConcurrentBag();
列表结果2=新列表();
公共异步任务扫描(字符串路径)
{
等待任务。运行(异步()=>
{
var subs=Directory.GetDirectories(路径);
等待任务.whalll(subs.Select(s=>Scan(s));
Add(Enumerable.Range(01000).Sum(i=>path[i%path.Length]).ToString());
});
}
公共无效扫描2(字符串路径)
{
Add(Enumerable.Range(01000).Sum(i=>path[i%path.Length]).ToString());
var subs=Directory.GetDirectories(路径);
foreach(潜艇中的var s)扫描2(s);
}
私有异步无效按钮4\u单击(对象发送方,事件参数e)
{
string dir=@“d:\tmp”;
System.Diagnostics.Stopwatch st=新的System.Diagnostics.Stopwatch();
st.Start();
等待扫描(dir);
st.Stop();
Show(st.elapsedmillesons.ToString());
st=新系统.Diagnostics.Stopwatch();
st.Start();
Scan2(dir);
st.Stop();
Show(st.elapsedmillesons.ToString());
MessageBox.Show(result.OrderBy(x=>x).SequenceEqual(result2.OrderBy(x=>x))?“确定”:“错误”);
}

我怀疑启动这么多任务(,而不是创建任务实例和
.Start
启动它们)会造成线程池作业积压,从而导致线程池不足(线程池有有限数量的线程和一个队列…只有当队列没有缩小时,它才会触发额外的线程…此测量需要时间,从而导致应用程序中的延迟)真的没有理由手工为IO生成额外的线程…你的应用程序能够跟上。考虑专用的<代码>异步代码>代码……你正在使用的文件IO命令的版本。停止滥用线程。BTW我投票关闭,因为这个问题是关于文件IO的,没有关于文件IO任何地方的代码。你的问题。滥用线程非常切题。线程是用来分散工作负载的,这样做是有代价的。这里没有工作负载可以分散,所以你得到的一切都是有代价的。这个问题不是关于文件
IO
,而是关于使用多线程的递归算法。我想我已经说得很清楚了。现在我正在fil上使用算法e系统但这是一个独立于
IO
的抽象层,我可以将底层系统切换到其他系统。我想没什么要说的了,你只是回到了我的信仰,所以,你是一个英雄!:)好吧,select方法对我不起作用,但我将其更改为以前的
任务[]
方法,它就像一个符咒。我明天会接受你的回答,因为如果你愿意的话,我想增加一些额外的赏金:)