我正在尝试编写一个多线程遍历树结构的C#系统。另一种看待这种情况的方法是BlockingCollection的消费者也是生产者。
我遇到的问题是什么时候完成。 我真正需要的测试是查看所有线程是否都在TryTake上。 如果它们已经完成了,那么我找不到一种方法来测试它,或者用有助于实现这一点的任何东西包装它。
下面的代码是我所拥有的这个代码的一个非常简单的例子,但是有一个条件,这个代码可能会失败。如果第一个线程刚刚通过了test.TryTake(out v,-1)并且还没有执行s.Release();它只是从集合中提取了最后一项,第二个线程只执行了if(s.CurrentCount == 0&amp;&amp; test.Count == 0),这可能会返回true,并且错误地开始完成。< / p>
但是第一个线程会继续,并尝试向集合中添加更多内容。
如果我可以划线:
if (!test.TryTake(out v, -1))
break;
s.Release();
atomic然后我相信这段代码会起作用。 (这显然是不可能的。)
但我无法弄清楚如何解决这个缺陷。
class Program
{
private static BlockingCollection<int> test;
static void Main(string[] args)
{
test = new BlockingCollection<int>();
WorkClass.s = new SemaphoreSlim(2);
WorkClass w0 = new WorkClass("A");
WorkClass w1 = new WorkClass("B");
Thread t0 = new Thread(w0.WorkFunction);
Thread t1 = new Thread(w1.WorkFunction);
test.Add(10);
t0.Start();
t1.Start();
t0.Join();
t1.Join();
Console.WriteLine("Done");
Console.ReadLine();
}
class WorkClass
{
public static SemaphoreSlim s;
private readonly string _name;
public WorkClass(string name)
{
_name = name;
}
public void WorkFunction(object t)
{
while (true)
{
int v;
s.Wait();
if (s.CurrentCount == 0 && test.Count == 0)
test.CompleteAdding();
if (!test.TryTake(out v, -1))
break;
s.Release();
Console.WriteLine(_name + " = " + v);
Thread.Sleep(5);
for (int i = 0; i < v; i++)
test.Add(i);
}
Console.WriteLine("Done " + _name);
}
}
}
答案 0 :(得分:0)
这可以使用任务并行来并行化。树中的每个节点都被认为是可能产生子任务的任务。有关更详细的说明,请参阅Dynamic Task Parallelism。
对于具有5个级别的二进制树,将每个节点写入控制台并等待5毫秒,如示例所示,ParallelWalk方法将查找示例如下:
class Program
{
internal class TreeNode
{
internal TreeNode(int level)
{
Level = level;
}
internal int Level { get; }
}
static void Main(string[] args)
{
ParallelWalk(new TreeNode(0));
Console.Read();
}
static void ParallelWalk(TreeNode node)
{
if (node == null) return;
Console.WriteLine(node.Level);
Thread.Sleep(5);
if(node.Level > 4) return;
int nextLevel = node.Level + 1;
var t1 = Task.Factory.StartNew(
() => ParallelWalk(new TreeNode(nextLevel)));
var t2 = Task.Factory.StartNew(
() => ParallelWalk(new TreeNode(nextLevel)));
Task.WaitAll(t1, t2);
}
}
中心线是生成任务t1和t2的地方。
通过任务中的这种分解,调度由Task Parallel Library完成,您不再需要管理共享的节点集。