c# - 如何制定这个多线程编程场景?

标签 c# multithreading

我正在尝试使用多线程来遍历树结构。这里的问题是,如果没有 HTTP 调用,树的结构是未知的(即,HTTP 调用将为您提供节点的子节点)。因此,我正在尝试使用多线程来增加我们可以发出的 HTTP 请求的吞吐量。

我不知道我们应该如何很好地解决这个问题,所以我先在这里描述一下我的想法。

最初我认为它与我们通常在 BFS 中编写的内容类似,假设我们的并发级别为 10。

SemaphoreSlim semaphore = new SemaphoreSlim(10);

Task HTTPGet(Node node) {
  blah blah
  Q.push(childNodes);
}

while (!Q.isEmpty()) {
    Node node = Q.head();
    Q.pop();
    taskList.Add(Task.Start(() => HTTPGet(node));
}

这里的问题是:处理完第一个节点后,Q变为空,整个循环终止。所以我觉得我们还需要检查信号量的剩余计数。因此,如果信号量的剩余计数不是 10,则表示某个进程仍在工作,我们应该等待它的进程。

while (!Q.isEmpty() || semaphore.Count != 10) {
    Node node = Q.head();
    Q.pop();
    taskList.Add(Task.Start(() => HTTPGet(node));
}

但显然在第一个节点被弹出后,Q 仍然是空的,我们需要在 while 循环中做一些“等待”以确保我们可以获取节点。

while (!Q.isEmpty() || semaphore.Count != 10) {
    if (Q.isEmpty()) {
       Wait till Q becomes non empty
       or semaphore.Count == 10 again
    }
    Node node = Q.head();
    Q.pop();
    taskList.Add(Task.Start(() => HTTPGet(node));
}

但是这会变得很丑陋,我很确定应该有更好的方法来解决这个问题。我试图在生产者-消费者范式中制定它但失败了(因为这次消费者也将启动更多生产者)。

有没有更好的方法来表述这个问题?

最佳答案

通过代码更容易解​​释,但请注意,这不是我尝试或测试过的东西。这是为了让你在正确的道路上入门

class Program {

    static void Main(string[] args) {
        new Program();
    }

    Program() {
        Node root = new Node("root");
        root.Children = new Node[2];
        root.Children[0] = new Node("child0");
        root.Children[1] = new Node("child1");

        MultiThreadedBFS(root);

    }


    BlockingCollection<Node> Queue = new BlockingCollection<Node>(10); // Limit it to the number of threads

    Node[] HTTPGet(Node parentNode) {
        return parentNode.Children; //your logic to fetch nodes go here
    }

    volatile int ThreadCount;

    void MultiThreadedBFS(Node root) {
        Queue.Add(root);

        // we fetch each node's children on a separate thread. 
        // This means that when all nodes are fetched, there are 
        // no more threads left. That will be our exit criteria
        ThreadCount = 0;

        do {
            var node = FetchNextNode();
            if (node == null)
                break;

            ProcessNode(node);
        } while (true);

    }

    Node FetchNextNode() {
        Node node;
        while (!Queue.TryTake(out node, 100)) {
            if (ThreadCount == 0 && Queue.Count == 0)
                return null; // All nodes have been fetched now
        }

        return node;
    }

    void ProcessNode(Node node) {
        // you can use a threadpool or task here
        new Thread(() => {
            Thread.CurrentThread.Name = "ChildThread";

            ++ThreadCount;
            Debug.WriteLine("Retrieving children for Node: " + node);
            var children = HTTPGet(node);
            foreach (var child in children) {
                Debug.WriteLine("Adding node for further processing: " + node);
                while (!Queue.TryAdd(child, -1))
                    ;
            }

            --ThreadCount;

        }).Start();
    }

    // this is the actual node class that represents the Node on the tree
    [DebuggerDisplay("Name = {Name}")]
    class Node {
        public string Name;
        public Node[] Children = new Node[0];

        public Node(string name) {
            Name = name;
        }

        public override string ToString() {
            return Name;
        }
    }

}

编辑:

我现在已经更新程序以修复退出条件和其他一些错误

此外,尽管我在这里使用的是线程,但我认为这是使用 async/await 的完美案例。我会让其他人使用 async/await 来回答

关于c# - 如何制定这个多线程编程场景?,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/35612653/

相关文章:

c# mysql incrementing null 错误

c# - HttpClient 取消不会终止底层 TCP 调用

c# - 为操作设置超时

在这种情况下我可以不使用发布和获取障碍吗?

c# - 按多列对 DataTable 进行分组并连接字符串

c# - 有没有办法通过 C# 中的 Type.InvokeMember 或消息调用窗体或类的私有(private)函数?

c# - EF LINQ 翻译 : complex query

c++ - 我在这里没有看到比赛条件?

java - arraylist/atomic double array (Google Guava) 中的 .get() 操作是线程安全的吗?

java - ExecutorService - 并行运行任务并保存结果