【问题标题】:How do I check that my multithreaded code is actually running on multiple threads?如何检查我的多线程代码是否实际在多个线程上运行?
【发布时间】:2010-07-27 02:40:16
【问题描述】:

我的程序有一个包含 200k 文件的列表。我必须将每个导入数据库。我花了很长时间,所以我开始研究多线程作为加快导入过程的一种手段。我终于实现了,但我不确定它是否真的有效。

使用Workaround for the WaitHandle.WaitAll 64 handle limit? 作为我的 c# 代码示例后,我想出了:

 int threadCount = 0;       

 for (int i = 0; i < this.Total; i++)
 {
       Finished = new ManualResetEvent(false);
       threadCount = this.ThreadCount;
       Interlocked.Increment(ref threadCount);

       FileHandler fh = new FileHandler(finished, sorted[i], this.PicturesFeatures, this.Outcome, this.SiteIds, this.LastId, this.Order, this.ThreadCount);
       Console.Write(i + " ");
       ThreadPool.QueueUserWorkItem(new WaitCallback(HandleFile), fh);
       Console.Write(i + " ");
       Finished.WaitOne();
 }

HandleFile() 如下:

 private void HandleFile(object s)
    {           
        try
        {
            //code        
        }
        finally
        {
            if (Interlocked.Decrement(ref threadCount) == 0)
            {
                Finished.Set();
            }
        }
    }

我已经把那些控制台写了,如果一个进程更长,它会比其他进程更晚完成(“0 0 1 2 2 1 3 3 ...”),但它总是有序的(“0 0 1 1 2 2 3 3 4 4 ...")

【问题讨论】:

  • 我想说一句:你确定你的代码花时间计算东西,而不是文件 I/O 和数据库操作?否则,请注意并行执行可能不会过多地加速您的代码这一事实(即,并行读取文件并不比逐个读取文件更快;除非文件存储在不同的硬盘上磁盘)。
  • 是的,但是数据库呢?每个线程打开一个不同的数据库连接,所以如果我使用并行,它应该会加快速度。
  • 不太确定。测试看看会发生什么。在所有情况下,如果瓶颈是磁盘 I/O 操作(本地或数据库级别),实现并行只会减慢速度。

标签: c# multithreading


【解决方案1】:

您的输出是预期的。您正在主线程中编写输出,QueueUserWorkItem 函数不会阻塞,它会注册您的 HandleFile 函数以在单独的线程中执行。因此,无论工作项花费多长时间,您的打印都将按照预期的顺序进行,因为它们都来自主线程。

此外,您并没有从使用此代码的并行性中受益,因为您在提交每个项目后都在等待。您实际上是在说我不会在最后一个工作项目完成之前提交我的下一个工作项目。这只是编写普通序列化代码的一种更复杂的方式。为了引入并行性,您需要将多个项目添加到队列中而无需等待。

【讨论】:

    【解决方案2】:

    在运行时进入执行(ctrl + alt + break),然后查看线程窗口。 (调试 -> Windows -> 线程)。

    【讨论】:

    • 好的。我有主线程、5 个带有 的工作线程、1 个名为 .NET SystemEvents 的工作线程和另一个名为 vshost.RunParkingWindow 的工作线程。我与一些 Hello World 程序相比,似乎没有太多的多线程在进行。
    • 您的线程执行速度有多快?尝试在您非常确定您有多个线程同时运行的地方设置一个断点。您也可以双击线程窗口中的项目,它会跳转到该线程的执行点。
    【解决方案3】:

    你有几个问题。

    • 工作项将有效地序列化,因为您要等待每个工作项完成,然后再开始下一个工作。
    • Console.WriteLine 调用在主线程上,因此它们很自然地将i 报告为按顺序递增。

    这是正确执行此操作的规范模式。

    int count = TOTAL_ITERATIONS;
    var finished = new ManualResetEvent(false);
    for (int i = 0; i < TOTAL_ITERATIONS; i++) 
    { 
      int captured = i; // Use this for variable capturing in the anonymous method.
      ThreadPool.QueueUserWorkItem(
        delegate(object state)
        {
          try
          {
            Console.WriteLine(captured.ToString());
            // Your task goes here.
            // Refer to 'captured' instead of 'i' if you need the loop variable.
            Console.WriteLine(captured.ToString());
          }
          finally
          {
            if (Interlocked.Decrement(ref count) == 0)
            {
              finished.Set();
            }
          }
        });
    }
    finished.WaitOne();
    

    编辑:要轻松演示调用多个线程,请使用以下代码。

    public static void Main()
    {
        const int WORK_ITEMS = 100;
        int count = WORK_ITEMS;
        var finished = new ManualResetEvent(false);
        Console.WriteLine(Thread.CurrentThread.ManagedThreadId.ToString() + ":Begin queuing...");
        for (int i = 0; i < WORK_ITEMS; i++)
        {
            int captured = i; // Use this for variable capturing in the anonymous method. 
            ThreadPool.QueueUserWorkItem(
              delegate(object state)
              {
                  try
                  {
                      Console.WriteLine(Thread.CurrentThread.ManagedThreadId.ToString() + ":" + captured.ToString());
                      for (int j = 0; j < 100; j++) Thread.Sleep(1);
                      Console.WriteLine(Thread.CurrentThread.ManagedThreadId.ToString() + ":" + captured.ToString());
                  }
                  finally
                  {
                      if (Interlocked.Decrement(ref count) == 0)
                      {
                          finished.Set();
                      }
                  }
              });
        }
        Console.WriteLine(Thread.CurrentThread.ManagedThreadId.ToString() + ":...end queueing");
        finished.WaitOne();
        Console.ReadLine();
    }
    

    【讨论】:

    • 我已按照您的建议进行了更改。然后我使用了线程窗口(正如菲尔在下面的答案中指出的那样),正如我告诉菲尔,似乎没有太大的效果。
    • @EduardoMello:这可能是因为ThreadPool 选择仅在池中的一个线程上串行执行工作项。我编辑了我的答案以包含强制将工作项分配给不同线程的代码,并通过将线程 ID 写入控制台来证明它确实这样做了。
    【解决方案4】:

    首先,从多线程应用程序产生的线程不能保证以任何特定顺序完成。您可能先启动了一个线程,但不一定先完成。

    话虽如此,您可以使用 Process Explorer:http://technet.microsoft.com/en-us/sysinternals/bb896653.aspx

    Process Explorer 将显示您的程序正在生成哪些线程。

    【讨论】:

      【解决方案5】:

      您输出的信息都来自同一个线程(运行循环的那个)。如果您想查看多个线程的证据,您可以从 HandleFile 函数中输出线程名称或其他值。

      【讨论】:

        猜你喜欢
        • 2016-09-19
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 2018-11-05
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        相关资源
        最近更新 更多