Warning: file_get_contents(/data/phpspider/zhask/data//catemap/2/csharp/320.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# 递归与Rx并行_C#_Multithreading_Performance_Parallel Processing_System.reactive - Fatal编程技术网

C# 递归与Rx并行

C# 递归与Rx并行,c#,multithreading,performance,parallel-processing,system.reactive,C#,Multithreading,Performance,Parallel Processing,System.reactive,在尝试有效地遍历目录树的同时,我尝试了一个描述的RX解决方案。虽然此解决方案适用于小树深度,但不适用于大树深度。默认调度程序创建的线程太多,减慢了树的遍历速度 以下是我使用的代码: public static void TestTreeTraversal() { Func<DirectoryInfo, IObservable<DirectoryInfo>> recurse = null; recurse = i => Obse

在尝试有效地遍历目录树的同时,我尝试了一个描述的RX解决方案。虽然此解决方案适用于小树深度,但不适用于大树深度。默认调度程序创建的线程太多,减慢了树的遍历速度

以下是我使用的代码:

public static void TestTreeTraversal()
    {
        Func<DirectoryInfo, IObservable<DirectoryInfo>> recurse = null;
        recurse = i => Observable.Return(i)
                        .Concat(i.GetDirInfos().ToObservable().SelectMany(d => recurse(d)))
                        .ObserveOn(Scheduler.Default);
        var obs = recurse(new DirectoryInfo(@"C:\"));
        var result = obs.ToEnumerable().ToList();
    }

public static IEnumerable<DirectoryInfo> GetDirInfos(this DirectoryInfo dir)
    {
        IEnumerable<DirectoryInfo> dirs = null;
        try
        {
            dirs = dir.EnumerateDirectories("*", SearchOption.TopDirectoryOnly);
        }
        catch (Exception)
        {
            yield break;
        }
        foreach (DirectoryInfo d in dirs)
            yield return d;
    }
publicstaticvoidtesttreetraversal()
{
Func recurse=null;
递归=i=>Observable.Return(i)
.Concat(i.GetDirInfos().ToObservable().SelectMany(d=>recurse(d)))
.ObserveOn(Scheduler.Default);
var obs=recurse(新目录信息(@“C:\”);
var result=obs.ToEnumerable().ToList();
}
公共静态IEnumerable GetDirInfos(此目录信息目录)
{
IEnumerable dirs=null;
尝试
{
dirs=dir.EnumerateDirectory(“*”,SearchOption.TopDirectoryOnly);
}
捕获(例外)
{
屈服断裂;
}
foreach(目录中的目录信息d)
收益率d;
}
如果删除ObserveOn(Scheduler.Default),该函数的工作速度与单线程递归函数相同。使用ObserveOn,似乎每次调用SelectMany时都会创建一个线程,这大大降低了进程的速度

是否有方法控制/限制调度程序可同时使用的最大线程数


有没有另一种方法可以用Rx编写这样的并行树遍历,而不会陷入这种并行陷阱

它可以在Rx中通过将
Environment.ProcessorCount
传递到
maxConcurrent
参数来完成

但是,Rx设计用于在本地异步处理的
IObservable
上工作。当然,您可以将
IEnumerable
转换为
IObservable
并并行处理它,就像您在这里所做的那样,但这与Rx中的粒度背道而驰

这个问题的一个更自然的解决方案是,它从一个
IEnumerable
开始,设计用于将查询划分为并行进程,隐式地考虑可用的物理处理器的数量

Rx主要是关于驯服并发性,而PLINQ主要是关于引入它

未经测试

Func<DirectoryInfo, ParallelQuery<DirectoryInfo>> recurse = null;

recurse = dir => new[] { dir }.AsParallel()
  .Concat(dir.GetDirInfos().AsParallel().SelectMany(recurse));

var result = recurse(new DirectoryInfo(@"C:\")).ToList();
Func recurse=null;
recurse=dir=>new[]{dir}.AsParallel()
.Concat(dir.GetDirInfos().AsParallel().SelectMany(recurse));
var result=recurse(新目录信息(@“C:\”)).ToList();

我已将GetDirInfos翻译成一个可观察的版本,并尝试使用Merge(8)。同样的问题也会发生:创建的线程太多。这样调用:Concat(i.getobservedirinfos().Select(d=>recurse(d)).Merge(8)).ObserveOn(Scheduler.Default);您未经测试的代码不起作用。我已经有了一个使用GetConsumingPartitioner()扩展名在BlockingCollection上使用parallel.ForEach的递归并行版本。我希望得到的是相同的结果,但是使用反应式扩展。您是如何将
GetDirInfos
转换为可观察的?您是否在利用本机文件I/O异步?如果没有,那么我建议使用PLINQ仍然有效。如果是这样,那么您的问题是您正在递归地应用
Merge
。Rx不限制对
Merge
的调用之间的并发性。您只需将
Merge
应用于查询的结尾。请记住,即使使用
Merge
也需要您自己制定分区策略。也许这就是困惑。您不能只将
Merge
插入到现有查询中。该查询专门为每个目录引入并发性。这就是为什么PLINQ更合适的原因。我在Concat()方法中使用Merge,因为Concat()将IObservable作为参数。我知道递归是问题的根源,在SelectMany()或Merge()方法中创建了多个线程。我还知道还有其他方法可以进行并行树遍历(我重复一遍,我已经有了一个可行的方法)。我要问的是:“有没有另一种方法可以用Rx编写这样的并行树遍历”
SelectMany
Merge
都没有引入并发性。
ObserveOn
操作符是唯一引入并发性的操作符,它对每个目录都这样做。这就是您选择使用的查询。如果您的问题是Rx是否可以为您划分序列,那么答案是否定的。您必须自己选择要并行执行的递归部分。