等待所有线程完成的有用模式是什么?

发布于 2024-08-21 21:48:45 字数 144 浏览 2 评论 0原文

我有一个场景,我必须启动大量线程(可能最多 100 个),然后等待它们完成,然后执行任务(在另一个线程上)。

从事此类工作的公认模式是什么?是简单的.Join吗?或者现在有更高层次的抽象吗?

将 .NET 2.0 与 VS2008 结合使用。

I have a scenario where I will have to kick off a ton of threads (possibly up to a 100), then wait for them to finish, then perform a task (on yet another thread).

What is an accepted pattern for doing this type of work? Is it simply .Join? Or is there a higher level of abstraction nowadays?

Using .NET 2.0 with VS2008.

如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。

扫码二维码加入Web技术交流群

发布评论

需要 登录 才能够评论, 你可以免费 注册 一个本站的账号。

评论(4

烛影斜 2024-08-28 21:48:45

在 .NET 3.5sp1 或 .NET 4 中,TPL< /a> 会让这变得更容易。不过,我将仅针对 .NET 2 功能进行定制。

有几个选择。使用 Thread.Join 是完全可以接受的,特别是如果线程都是您手动创建的线程。这是非常容易、可靠且易于实施的。这可能是我的选择。

然而,另一种选择是为总工作量创建一个计数器,并在计数器达到零时使用重置事件。例如:

class MyClass {
    int workToComplete; // Total number of elements
    ManualResetEvent mre; // For waiting

    void StartThreads()
    {
        this.workToComplete = 100;
        mre = new ManualResetEvent(false);

        int total = workToComplete;
        for(int i=0;i<total;++i)
        {
             Thread thread = new Thread( new ThreadStart(this.ThreadFunction) );
             thread.Start(); // Kick off the thread
        }

        mre.WaitOne(); // Will block until all work is done
    }

    void ThreadFunction()
    {
        // Do your work

        if (Interlocked.Decrement(ref this.workToComplete) == 0)
            this.mre.Set(); // Allow the main thread to continue here...
    }
}

In .NET 3.5sp1 or .NET 4, the TPL would make this much easier. However, I'll tailor this to .NET 2 features only.

There are a couple of options. Using Thread.Join is perfectly acceptable, especially if the threads are all ones you are creating manually. This is very easy, reliable, and simple to implement. It would probably be my choice.

However, the other option would be to create a counter for the total amount of work, and to use a reset event when the counter reaches zero. For example:

class MyClass {
    int workToComplete; // Total number of elements
    ManualResetEvent mre; // For waiting

    void StartThreads()
    {
        this.workToComplete = 100;
        mre = new ManualResetEvent(false);

        int total = workToComplete;
        for(int i=0;i<total;++i)
        {
             Thread thread = new Thread( new ThreadStart(this.ThreadFunction) );
             thread.Start(); // Kick off the thread
        }

        mre.WaitOne(); // Will block until all work is done
    }

    void ThreadFunction()
    {
        // Do your work

        if (Interlocked.Decrement(ref this.workToComplete) == 0)
            this.mre.Set(); // Allow the main thread to continue here...
    }
}
戏舞 2024-08-28 21:48:45

你看过线程池吗?看起来像这里 -ThreadPool 教程,avtor 解决了与你问。

Did you look at ThreadPool? Looks like here -ThreadPool tutorial, avtor solves same task as you ask.

太阳哥哥 2024-08-28 21:48:45

对我来说效果好的方法是在启动每个线程时将其 ManagedThreadId 存储在字典中,然后让每个线程在完成时通过回调方法将其 id 传回。回调方法从字典中删除id并检查字典的Count属性;当它为零时,你就完成了。请务必锁定字典以添加和删除字典。

What's worked well for me is to store each thread's ManagedThreadId in a dictionary as I launch it, and then have each thread pass its id back through a callback method when it completes. The callback method deletes the id from the dictionary and checks the dictionary's Count property; when it's zero you're done. Be sure to lock around the dictionary both for adding to and deleting from it.

把时间冻结 2024-08-28 21:48:45

我不确定任何类型的标准线程锁定或同步机制是否真的适用于这么多线程。然而,在这种情况下,一些基本消息传递可能是问题的理想解决方案。

您可以尝试再设置一个线程来聚合来自工作线程的完成消息,而不是使用会阻塞的 Thread.Join(并且可能很难管理如此多的线程)。当聚合器收到所有预期消息时,它就完成了。然后,您可以在聚合器和主应用程序线程之间使用单个 WaitHandle 来发出所有工作线程已完成的信号。

public class WorkerAggregator
{
    public WorkerAggregator(WaitHandle completionEvent)
    {
        m_completionEvent = completionEvent;
        m_workers = new Dictionary<int, Thread>();
    }

    private readonly WaitHandle m_completionEvent;
    private readonly Dictionary<int, Thread> m_workers;

    public void StartWorker(Action worker)
    {
        var thread = new Thread(d =>
            {
                worker();
                notifyComplete(thread.ManagedThreadID);
            }
        );

        lock (m_workers)
        {
            m_workers.Add(thread.ManagedThreadID, thread);
        }

        thread.Start();
    }

    private void notifyComplete(int threadID)
    {
        bool done = false;
        lock (m_workers)
        {
            m_workers.Remove(threadID);
            done = m_workers.Count == 0;
        }

        if (done) m_completionEvent.Set();
    }
}

请注意,我尚未测试上面的代码,因此它可能不是 100% 正确。不过,我希望它足以说明这个概念,使其有用。

I am not sure that any kind of standard thread locking or synchronization mechanisms will really work with so many threads. However, this might be a scenario where some basic messaging might be an ideal solution to the problem.

Rather than using Thread.Join, which will block (and could be very difficult to manage with so many threads), you might try setting up one more thread that aggregates completion messages from your worker threads. When the aggregator has received all expected messages, it completes. You could then use a single WaitHandle between the aggregator and your main application thread to signal that all of your worker threads are done.

public class WorkerAggregator
{
    public WorkerAggregator(WaitHandle completionEvent)
    {
        m_completionEvent = completionEvent;
        m_workers = new Dictionary<int, Thread>();
    }

    private readonly WaitHandle m_completionEvent;
    private readonly Dictionary<int, Thread> m_workers;

    public void StartWorker(Action worker)
    {
        var thread = new Thread(d =>
            {
                worker();
                notifyComplete(thread.ManagedThreadID);
            }
        );

        lock (m_workers)
        {
            m_workers.Add(thread.ManagedThreadID, thread);
        }

        thread.Start();
    }

    private void notifyComplete(int threadID)
    {
        bool done = false;
        lock (m_workers)
        {
            m_workers.Remove(threadID);
            done = m_workers.Count == 0;
        }

        if (done) m_completionEvent.Set();
    }
}

Note, I have not tested the code above, so it might not be 100% correct. However I hope it illustrates the concept enough to be useful.

~没有更多了~
我们使用 Cookies 和其他技术来定制您的体验包括您的登录状态等。通过阅读我们的 隐私政策 了解更多相关信息。 单击 接受 或继续使用网站,即表示您同意使用 Cookies 和您的相关数据。
原文