【问题标题】:Get a thread id inside parallel.ForEach loop在 parallel.ForEach 循环中获取线程 ID
【发布时间】:2015-11-22 02:29:19
【问题描述】:

有没有办法在 Parallel.FoEach 循环中找到线程 ID。我尝试使用 var threadId = Thread.CurrentThread.ManagedThreadId - 1;,但它没有为我提供我正在寻找的正确索引。

这是一个简单的例子:

private void TestProgram()
    {
        int numThreads = 1;

        var values = new List<float>();
        for (int i = 0; i < numThreads; ++i)
        {
            values.Add(i);
        }

        var data = new List<int>();
        for (int i = 0; i < numThreads; ++i)
        {
            data.Add(i);
        }


        Parallel.ForEach(data, new ParallelOptions{MaxDegreeOfParallelism = numThreads}, i =>
            //foreach (var i in data)
        {
            var threadId = Thread.CurrentThread.ManagedThreadId - 1; // make the index to start from 0

            values[threadId] += i;
        });
    }

即使设置了MaxDegreeOfParallelism to 1,我仍然得到threadId 大于1。

在上述场景中,有没有办法在 Parallel.ForEach 中找到线程 ID?

注意:我可以在我使用的示例中使用 Parallel.For。但我的问题是在 Parallel.ForEach 中找到它

【问题讨论】:

标签: c# multithreading parallel.foreach


【解决方案1】:

由于 Parallel.ForEach 是任务库的一部分,Task.CurrentId 将使您更接近您正在寻找的内容:

   var data = new[] { 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 };


   Parallel.ForEach(data, new ParallelOptions { MaxDegreeOfParallelism = 4 }, i =>
   {
            Console.WriteLine(Task.CurrentId);
   });

输出为 1 1 1 1 1 1 1 1 1 1 2 2 1

但是,文档中有免责声明:

任务 ID 是按需分配的,不一定代表 创建任务实例的顺序。请注意,虽然 冲突非常罕见,任务标识符不保证是 独一无二。

【讨论】:

    【解决方案2】:

    ThreadID 由底层环境分配,不保证从 0 到 [线程数] 甚至每次运行都一致。

    只有少数几个关于 threadID 的合同,甚至这些合同也不能保证:

    • 你不会得到 ThreadID 0
    • ThreadID 1 通常保留给主线程

    【讨论】:

      【解决方案3】:

      您几乎总是会得到一个大于 1 的线程 ID。并行操作将安排在线程池线程上。由于这些线程是在应用程序启动后创建的,因此线程 ID 应该已经启动。

      【讨论】:

        【解决方案4】:
        var data = new[] { 0,0,0,0,0,0,0,0,0,0,0,0,0};
        
        
            Parallel.ForEach(data, new ParallelOptions{MaxDegreeOfParallelism = 10}, i =>
            {
                Console.WriteLine(System.Threading.Thread.CurrentThread.ManagedThreadId);
            });
        

        我得到输出:

        180 180 180 180 180 180 180 180 62 62 62 62 180

        这是典型的。这是一个环境 ID,与您的 foreach 循环几乎没有关系。您还可以看到,在这种情况下,.NET 不需要达到 MaxDegreeOfParallelism(这是您的另一个假设)。

        【讨论】:

          【解决方案5】:

          由于这在 .NET 中似乎不可用,只需将看似随机的值映射回从零开始的值。

          示例如下,您可以根据需要创建资源,每个线程一个。 'locker' 是为了防止线程相互干扰。另请注意,列表应替换为 System.Collections.Concurrent.ConcurrentBag 等线程安全对象

          object locker = new object();
          List<int> ids = new List<int>();
          List<object> resources = new List<object>();
          
          Parallel.For(0, 20000, x =>
          {
              int thread_id = ids.IndexOf(Environment.CurrentManagedThreadId);
              if (thread_id == -1)
              {                    
                  ids.Add(Environment.CurrentManagedThreadId);
                  thread_id = ids.IndexOf(Environment.CurrentManagedThreadId);                    
              }
          
              while (resources.Count < thread_id + 1)
              {
                  lock (locker)
                  {
                      resources.Add(new object());
                  }
              }
          
              object resource = resources[thread_id];
              // do stuff with the resource
          });
          

          【讨论】:

            猜你喜欢
            • 2015-11-01
            • 1970-01-01
            • 1970-01-01
            • 1970-01-01
            • 1970-01-01
            • 1970-01-01
            • 1970-01-01
            • 1970-01-01
            • 1970-01-01
            相关资源
            最近更新 更多