在parallel.ForEach循环中获取一个线程id

时间:2015-08-27 15:43:20

标签: c# multithreading parallel.foreach

有没有办法在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

中找到它

5 个答案:

答案 0 :(得分:5)

ThreadID由底层环境分配,并且不保证从0到[线程数],或者从运行到运行都保持一致。

只有少数与threadID有关的合同,即使这些也没有保证:

  • 您无法获得ThreadID 0
  • ThreadID 1通常保留给主线程

答案 1 :(得分:5)

由于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 :(得分:0)

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

答案 3 :(得分:0)

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(这是您的另一个假设)。

答案 4 :(得分:0)

由于这在 .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
});