.net 使用条件锁定控制并行性?
总而言之,我有一个递归任务,我想使用我所有的4个处理器来更快地处理这棵大树。我当前的生产实现使用了.net 使用条件锁定控制并行性?,.net,multithreading,c#-4.0,recursion,locking,.net,Multithreading,C# 4.0,Recursion,Locking,总而言之,我有一个递归任务,我想使用我所有的4个处理器来更快地处理这棵大树。我当前的生产实现使用了Parallel.ForEach,失去了控制,占用了我所有的4个CPU,很快就耗尽了内存。因此,我知道正确的算法可以以70-80%的速度为我提供所有4个CPU,我发现这样可以快速完成爬行任务,同时让UI响应性和我的计算机整体响应性更好,以完成轻UI用户驱动的任务。此任务是后台任务 我尝试的方法(如下所列)是并行和递归的,我想使用条件锁来限制线程 我希望这段代码最多使用4个线程来递归地创建20个可怕的
Parallel.ForEach
,失去了控制,占用了我所有的4个CPU,很快就耗尽了内存。因此,我知道正确的算法可以以70-80%的速度为我提供所有4个CPU,我发现这样可以快速完成爬行任务,同时让UI响应性和我的计算机整体响应性更好,以完成轻UI用户驱动的任务。此任务是后台任务
我尝试的方法(如下所列)是并行和递归的,我想使用条件锁来限制线程
我希望这段代码最多使用4个线程来递归地创建20个可怕的头,直到所有分支中的嵌套深度达到10为止。我把它从2头改为20头,因为这更像我的实际问题。我的实际树只有4-5层深,但相当宽,每个节点需要比控制台更多的cpu
这并不像我想象的那么容易实现
我试图让所有大于4的线程等待,直到有足够的线程在完成之前到达,使总线程数回到4,然后再继续。因此,如果创建了4个以上的线程就可以了,只要>#4个线程还在等待。因此有条件等待(锁定)部分
我的代码示例显然只是为了概念目的,并且正是我所尝试的。请随意偏离我的实现细节
编辑:我昨晚更改了我的实现,使用信号量lim(胖男孩斯利姆的表弟)来处理交通警察的角色。它只会导致两个处理器以20%的速度忙碌
我的下一次迭代可能会涉及四次循环,以创建4个独立于其他节点抓取节点的worker。但困难在于,他们需要知道哪些节点(子树)当前正在被爬网,或者已经被另一个工作者爬网。我不确定这是否比下面的方法简单。所列出的方法似乎确实避免了以错误的顺序处理节点(例如,子节点在父节点之前),但这可能只是代码结构外观带来的错觉
using System;
using System.Collections;
using System.Collections.Generic;
using System.Linq;
using System.Threading;
using System.Threading.Tasks;
public class ScaryTeddy
{
public ScaryTeddy(ScaryTeddy parent, int position)
{
Parent = parent;
Position = position;
DoSomethingHeavy();
}
public BlockingCollection<ScaryTeddy> Heads = new BlockingCollection<ScaryTeddy>();
public ScaryTeddy Parent { get; set; }
private string _path;
public string Path
{
get
{
if (_path == null)
{
if (Parent != null)
_path = string.Format("{0}.{1}", Parent.Path, Position);
else
_path = Position.ToString();
}
return _path;
}
}
public int Position { get; set; }
// short in duration but taxing on cpu and memory
private static void DoSomethingHeavy()
{
// look at all the text inside every jpg in my pictures. Admire my girl friend's beauty!
FileSystem.FindInFilesFileList(@"C:\Documents\Pictures", new List<string>() { "Exif" }, new List<string>() { "*.jpg" }, null, null);
}
// these have to be static b/c CreateScaryTeddy is static
private static readonly SemaphoreSlim SemaphoreSlim = new SemaphoreSlim(4, 4); // 4 cpus
private static object _lock = new object(); // one object for all instances? Is that correct?
private static int _scaryTeddyFactories = 0; // just a way to inspect how many are concurrent
// this only produces 2 cpus working at about 20%; I want all 4 working at 70-80%
public static ScaryTeddy CreateScaryTeddy(ScaryTeddy parent = null, int position = 1)
{
SemaphoreSlim.Wait();
lock (_lock) _scaryTeddyFactories++;
var scaryTeddy = new ScaryTeddy(parent, position);
Console.WriteLine("Thread {0} with slot {1} created Scary Teddy {2}", Thread.CurrentThread.ManagedThreadId, _scaryTeddyFactories, scaryTeddy.Path);
lock (_lock) _scaryTeddyFactories--;
SemaphoreSlim.Release();
if (scaryTeddy.Path.Split(".".ToCharArray()).Length <= 10)
{
Parallel.For(0, 20,
new ParallelOptions { MaxDegreeOfParallelism = 2 },
babyHead => scaryTeddy.Heads.Add(CreateScaryTeddy(scaryTeddy, babyHead)));
}
return scaryTeddy;
}
}
使用系统;
使用系统集合;
使用System.Collections.Generic;
使用System.Linq;
使用系统线程;
使用System.Threading.Tasks;
公共级Scarytedy
{
公共ScaryTeddy(ScaryTeddy父级,内部位置)
{
父母=父母;
位置=位置;
DoSomethingHeavy();
}
public BlockingCollection Heads=new BlockingCollection();
公共scarytedy父项{get;set;}
私有字符串路径;
公共字符串路径
{
得到
{
如果(_path==null)
{
如果(父项!=null)
_path=string.Format(“{0}.{1}”,Parent.path,Position);
其他的
_路径=位置.ToString();
}
返回路径;
}
}
公共int位置{get;set;}
//持续时间短,但占用cpu和内存
私有静态无效DoSomethingHeavy()
{
//看看我照片中每个jpg里面的文字。欣赏我女朋友的美丽!
FindInFilesFileList(@“C:\Documents\Pictures”,new List(){“Exif”},new List(){“*.jpg”},null,null);
}
//这些必须是静态的b/c CreateCaryteddy是静态的
私有静态只读信号量lim SemaphoreSlim=新信号量lim(4,4);//4个CPU
私有静态对象_lock=new object();//所有实例一个对象?正确吗?
private static int\u scarytedfactories=0;//这只是一种检查有多少是并发的方法
//这只会产生2个CPU,工作速度约为20%;我希望所有4个CPU的工作速度均为70-80%
公共静态ScaryTeddy CreateScaryTeddy(ScaryTeddy父项=null,内部位置=1)
{
SemaphoreSlim.Wait();
锁(_lock)_scarytedyFactorys++;
var scaryTeddy=新scaryTeddy(父级,位置);
WriteLine(“插槽为{1}的线程{0}创建了{2}”、Thread.CurrentThread.ManagedThreadId、_scarytedFactorys、scaryTeddy.Path);
锁(_lock)_scarytedy工厂--;
SemaphoreSlim.Release();
if(scaryTeddy.Path.Split(“.”.ToCharArray()).Length scarytedy.Heads.Add(CreateScaryTeddy(scaryTeddy,babyHead));
}
返回scarytedy;
}
}
编辑:结果
所有4个处理器几乎都被锁定-完美
控制台输出显示涉及线程池。我猜信号灯的工作方式是打开的插槽总是#4
线程1与插槽1一起创建
带插槽2的螺纹6创建了Teddy 1.10
带有插槽3的线程1已创建为Teddy 1.0
线程“”(0x1668)已退出,代码为0(0x0)。
线程“”(0x3bd0)已退出,代码为0(0x0)。
带插槽4的螺纹5由Teddy 1.10.0创建
带插槽4的螺纹1创建于Teddy 1.0.10
螺纹6和槽4创建于Teddy 1.10.10
带插槽4的线程3已创建为Teddy 1.0.0
螺纹5和插槽4创建在Teddy 1.10.0.0中
带插槽4的螺纹1创建于Teddy 1.0.10.0
带插槽4的螺纹9由Teddy 1.0.10.10创建
螺纹6和插槽4创建在Teddy 1.10.10.0中
我们有4个线程在工作,这正是我想要的,而池中的其他线程正在等待,这并不太多。用新的信号量lim(4)替换您的
失控的节流机制。这是框架内置的。(另外,您正在读取\u scaryTeddyFactoryCount
,但未锁定
Thread 1 with slot 1 created Scary Teddy 1
Thread 6 with slot 2 created Scary Teddy 1.10
Thread 1 with slot 3 created Scary Teddy 1.0
The thread '<No Name>' (0x1668) has exited with code 0 (0x0).
The thread '<No Name>' (0x3bd0) has exited with code 0 (0x0).
Thread 5 with slot 4 created Scary Teddy 1.10.0
Thread 1 with slot 4 created Scary Teddy 1.0.10
Thread 6 with slot 4 created Scary Teddy 1.10.10
Thread 3 with slot 4 created Scary Teddy 1.0.0
Thread 5 with slot 4 created Scary Teddy 1.10.0.0
Thread 1 with slot 4 created Scary Teddy 1.0.10.0
Thread 9 with slot 4 created Scary Teddy 1.0.10.10
Thread 6 with slot 4 created Scary Teddy 1.10.10.0