繁体   English   中英

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

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

有没有办法在 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设置MaxDegreeOfParallelism to 1 ,我仍然让threadId大于 1。

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

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

由于 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 是按需分配的,不一定代表创建任务实例的顺序。 请注意,虽然冲突非常罕见,但不能保证任务标识符是唯一的。

ThreadID 由底层环境分配,并不能保证从 0 到 [线程数] 甚至从运行到运行是一致的。

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

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

您几乎总是会得到一个大于 1 的线程 ID。 并行操作将在线程池线程上调度。 由于这些线程是在应用程序启动后创建的,因此线程 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);
    });

我得到输出:

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

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

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

暂无
暂无

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

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