【问题标题】:Multi thread worker thread status多线程工作线程状态
【发布时间】:2011-04-04 18:45:01
【问题描述】:

我将线程创建为

 for (int i = 0; i < threadCount; i++)
 {
     Searcher src = new Searcher(i, this);

     threads[i] = new Thread(new ThreadStart(src.getIpRange));
     threads[i].Name = string.Format(i.ToString());
  }

  foreach (Thread t in threads)
  {
     t.Start();
  }

使用 threadCount(= 100, 150, 255 等...) 但我不知道有多少线程在工作。执行时间。

我想控制所有线程何时完成工作。并给我一条消息,例如“所有线程都已死,作业已完成......” 比如 backgroundWorker 的 RunWorkerCompleted 事件

【问题讨论】:

  • 我很难想象一个应用程序有 255 个相同的线程是最佳的。请记住,在任何给定时间可以在系统范围内运行的最大线程数 = 目标硬件中独立 CPU 内核的数量。 fwiw 根据我的经验(Win XP SP2),线程创建在单个进程中大约 120 之后失败。这是在一个测试应用程序中实现的,每个基于套接字的应用程序客户端一个线程。我不可能那样实现实际的生产代码。
  • 我正在开发一个网络应用程序,因此我的线程花费了很多时间。我认为这不是 cpu 的过载

标签: c# .net multithreading threadpool thread-state


【解决方案1】:

首先,我必须指出,创建 100、150、255 等线程可能不是一个好主意。最好使用ThreadPoolTask 类(如果使用.NET 4.0)。除此之外,还有两种成熟的方法可以等待所有线程完成。

加入话题。

Thread.Join 阻塞,直到目标线程完成。

for (int i = 0; i < threadCount; i++) 
{ 
   Searcher src = new Searcher(i, this); 
   threads[i] = new Thread(new ThreadStart(src.getIpRange)); 
   threads[i].Name = string.Format(i.ToString()); 
} 

foreach (Thread t in threads) 
{ 
   t.Start(); 
} 

foreach (Thread t in threads)
{
   t.Join(); 
}

使用倒计时事件。

CountdownEvent 一直等待,直到其内部计数达到零。如果您想使用ThreadPool,此方法更适合。如果您不使用 .NET 4.0,您可以在 Joe Albahari's website 获得一个非常简单的实现。

var finished = new CountdownEvent(1);

for (int i = 0; i < threadCount; i++) 
{ 
   finished.AddCount();
   Searcher src = new Searcher(i, this); 
   threads[i] = new Thread(
     () =>
     {
        try
        {
          src.getIpRange();
        }
        finally
        {
          finished.Signal();
        }
     }
   threads[i].Name = string.Format(i.ToString()); 
} 

foreach (Thread t in threads) 
{ 
   t.Start(); 
} 

finished.Signal();
finished.WaitOne();

【讨论】:

  • 是.net 3.5中定义的倒计时事件吗?
  • 不,我更新了我的答案以包含一个简单实现的链接。
【解决方案2】:

向 Searcher 添加一个委托,并从主线程向其传递一个回调方法,每个线程在完成时都会调用该方法。启动每个线程时,将其添加到由线程的 ManagedThreadId 键入的 Dictionary 中。当每个线程完成时,回调从 Dictionary 中删除线程并检查计数是否为零。

        Dictionary<int, Thread> activeThreads = new Dictionary<int, Thread>();            

           for (int i = 0; i < threadCount; i++)
            {
                Searcher src = new Searcher(i, this);
                src.Done = new SearcherDoneDelegate(ThreadDone);
                threads[i] = new Thread(new ThreadStart(src.getIpRange));
                threads[i].Name = string.Format(i.ToString());
            }

            foreach (Thread t in threads)
            {
                lock (activeThreads)
                {
                    activeThreads.Add(t.ManagedThreadId, t);
                }
                t.Start();
            }


        }
        public void ThreadDone(int threadIdArg)
        {
            lock (activeThreads)
            {
                activeThreads.Remove(threadIdArg);
                if (activeThreads.Count == 0)
                {
                    // all done
                }
            }
        }

        public delegate void SearcherDoneDelegate(int threadIdArg);
        public static object locker = new object();



        public class Searcher
        {
            public SearcherDoneDelegate Done { get; set; }
            public void getIpRange()
            {
                Done(Thread.CurrentThread.ManagedThreadId);
            }
        }

如果您的线程数多于您一次想要运行的线程数,请将它们放入队列中,并在旧线程完成时将它们剥离(使用回调)。

【讨论】:

  • 感谢您的解决方案,但 (activeThreads.Count == 0) 无法正常工作,很多时候都失败了!这是我的代码 if ((activeThreads.Count) == 0) { j++;进度条1.Value = 0; btnSearch.Enabled = true;在这种情况下progressbar1。值得到 0 并在再次得到 0 后多次成长。而且我无法确定线程是否已完成
  • @Rapunzo - 发生的事情是您在创建每个线程后立即启动它,并且初始线程在后面的线程开始之前完成。解决这个问题的快速方法是将 t.Start() 移动到第二个循环,以便在启动它们之前创建所有它们。更优雅的方法是将创建的线程放在 Queue 中,并使用生产者-消费者模式读取它们。这也让您可以控制并发线程的数量。
【解决方案3】:

您肯定希望为此使用Task 类或Parallel.ForEach 等更高级别的概念。直接使用Thread 类是相当痛苦的。

我最近 wrote a blog post 比较了各种异步方法,按从最好 (Task) 到最差 (Thread) 的顺序列出。

这是一个使用Task 的示例,展示了您想要做什么:

// Start all tasks
var threads = new Task[threadCount];
for (int i = 0; i < threadCount; i++) 
{ 
  Searcher src = new Searcher(i, this); 
  threads[i] = Task.Factory.StartNew(src.getIpRange);
}

// How many are running right now?
var runningCount = threads.Count(x => x.Status == TaskStatus.Running);

// Register a callback when they have all completed (this does not block)
Task.Factory.ContinueWhenAll(threads, MyCallback);

【讨论】:

  • 我尝试使用 System.Threading.Tasks 添加;到我的项目但收到错误:命名空间“System.Threading”中不存在类型或命名空间名称“Tasks”(您是否缺少程序集引用?)
  • 要使用 Task 类,您需要 .NET 4.0 或 .NET 3.5 的 Rx library
【解决方案4】:

您可以检查线程的ThreadState 属性。

使用异步方法可能会更好。这会为您提供一个WaitHandle 对象,您可以使用WaitHandle.WaitAll 等待所有异步方法完成。

这是异步编程的介绍: http://msdn.microsoft.com/en-us/library/aa719598%28v=VS.71%29.aspx

【讨论】:

  • 你能解释一下异步方法吗?
  • 您使用 BeginInvoke() 调用您的方法,而不是直接调用它
【解决方案5】:

确定所有线程何时完成很简单。

for (int i = 0; i < threadCount; i++)
{
    threads[i].Join();
}

Console.WriteLine("All threads are done!");

您能否详细说明您的其他要求?

【讨论】:

  • 我试过了,但阻止了进度。
  • @Rapunzo - 确实,这就是重点。 Thread.Join 是一个阻塞调用 - 因此 Console.WriteLine 在所有线程完成之前不会执行。您可以在另一个线程上运行这段代码,并在您选择的任何通知机制上进行适当的同步。为了简单起见,我在这里使用了Console.WriteLine
【解决方案6】:

为什么不能使用临界区受保护的单个变量来控制多个活动线程?线程函数可以修改这个变量(当然已经进入了临界区)。

【讨论】:

猜你喜欢
  • 2021-10-15
  • 2013-03-30
  • 1970-01-01
  • 2012-02-22
  • 2022-01-10
  • 1970-01-01
  • 1970-01-01
  • 2013-04-14
  • 2010-11-24
相关资源
最近更新 更多