简体   繁体   English

在 parallel.ForEach 循环中获取线程 ID

[英]Get a thread id inside parallel.ForEach loop

Is there a way to find the thread id inside Parallel.FoEach loop.有没有办法在 Parallel.FoEach 循环中找到线程 ID。 I tried using var threadId = Thread.CurrentThread.ManagedThreadId - 1;我尝试使用var threadId = Thread.CurrentThread.ManagedThreadId - 1; , but it didn't give me the correct index I was looking for. ,但它没有给我我正在寻找的正确索引。

Here is an simple example:这是一个简单的例子:

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;
        });
    }

Even after setting the MaxDegreeOfParallelism to 1 , I still get the threadId to be greater than 1.即使在将MaxDegreeOfParallelism to 1设置MaxDegreeOfParallelism to 1 ,我仍然让threadId大于 1。

Is there a way to find the thread id inside Parallel.ForEach in above scenario?有没有办法在上面的场景中找到 Parallel.ForEach 中的线程 ID?

Note: I could have used Parallel.For in the example I used.注意:我可以在我使用的示例中使用 Parallel.For。 But my question is to find it inside Parallel.ForEach但我的问题是在 Parallel.ForEach 中找到它

Since Parallel.ForEach is part of the Task Library, Task.CurrentId will get you closer to what you are looking for:由于 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);
   });

output is 1 1 1 1 1 1 1 1 1 1 2 2 1输出是 1 1 1 1 1 1 1 1 1 1 2 2 1

However, there is a disclaimer in the docs:但是,文档中有免责声明:

Task IDs are assigned on-demand and do not necessarily represent the order in which task instances are created.任务 ID 是按需分配的,不一定代表创建任务实例的顺序。 Note that although collisions are very rare, task identifiers are not guaranteed to be unique.请注意,虽然冲突非常罕见,但不能保证任务标识符是唯一的。

ThreadIDs are assigned by the underlying environment, and have no guarantee of being from 0 to [num of threads] or even being consistent from run to run. ThreadID 由底层环境分配,并不能保证从 0 到 [线程数] 甚至从运行到运行是一致的。

There's only a few contracts with respect to threadIDs, and even these aren't guaranteed:关于 threadID 的合同只有几个,甚至这些也不能保证:

  • You won't get a ThreadID 0你不会得到 ThreadID 0
  • ThreadID 1 is usually reserved for the Main thread ThreadID 1 通常保留给主线程

You will almost always get a thread ID that is greater than one.您几乎总是会得到一个大于 1 的线程 ID。 The parallel operations will be scheduled on a thread pool threads.并行操作将在线程池线程上调度。 Since those threads are created after the application has started a thread ID of one would already be started.由于这些线程是在应用程序启动后创建的,因此线程 ID 为 1 就已经启动了。

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);
    });

I get output:我得到输出:

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

This is typical.这是典型的。 It's an environmental ID and have little to do with your foreach loop.这是一个环境 ID,与您的 foreach 循环关系不大。 You can also see that .NET did not feel the need to go up to MaxDegreeOfParallelism in this case (which was another of your assumptions).您还可以看到,在这种情况下(这是您的另一个假设),.NET 觉得没有必要提高到 MaxDegreeOfParallelism。

Since this does not seem to be available in .NET just map the seemingly random values back to values which start at zero.由于这在 .NET 中似乎不可用,因此只需将看似随机的值映射回从零开始的值。

Example is below where you create resources as necessary, one for each thread.示例如下,您可以根据需要创建资源,每个线程一个。 The 'locker' is to prevent threads from interfering with each other. 'locker'是为了防止线程相互干扰。 Also note that Lists should be replaced with thread safe objects like System.Collections.Concurrent.ConcurrentBag另请注意,列表应替换为线程安全对象,如 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
});

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM