如何验证 n 个方法是否并行执行

本文关键字:方法 是否 并行执行 何验证 验证 | 更新日期: 2023-09-27 18:35:18

仅出于知识目的,如何验证n个方法是否并行执行?

Parallel.For(0, 10, i => DoSomething(i));
...
void DoSomething(int par)
{
    Console.WriteLine($"Test: {par}");
}

有没有一个测试可以向我保证 DoSomething 不是按顺序执行的(如果Console.WriteLine不是一个好的测试,那么一个非常简单的测试函数是什么?

如何验证 n 个方法是否并行执行

在您的示例中,方法肯定是顺序的,因为 .NET 控制台将同步来自不同线程的调用。

但是,您可以使用 Thread.ManagedThreadID 或 Environment.CurrentManagedThreadId(对于 .NET 4.5)检查运行该方法的线程 ID。

class Program
{
    static void Main(string[] args)
    {
        // first run with default number of threads
        Parallel.For(0, 10, i => DoSomething(i));
        Console.ReadLine();
        // now run with fewer threads...
        Parallel.For(0, 10, new ParallelOptions{ 
                                  MaxDegreeOfParallelism = 2 
                                }, i => DoSomething(i));
        Console.ReadLine();
    }
    static void DoSomething(int par)
    {
        int i = Environment.CurrentManagedThreadId;
        // int i = Thread.ManagedThreadId;  -- .NET 4.0 and under
        Thread.Sleep(200);
        Console.WriteLine("Test: "+ par.ToString() + 
                          ", Thread :" + i.ToString());
    }
}

如果在没有Thread.Sleep的情况下运行此操作,您会注意到只会使用几个线程,因为调用将足够快地完成,以便线程及时返回到池中,以拾取队列分发的下一个作业。

添加睡眠会将线程(模拟工作)延迟足够长的时间,以至于线程池必须获取更多线程才能及时启动作业。

并行运行任何任务时,很容易检查这些任务实际上是否都在单独的线程中运行。 但是,是否获得性能优势将取决于这些线程的工作效率。 如您所见,某些操作会调用共享资源上的争用(例如写入控制台),并可能阻止多个线程同时运行。

例如,其他争用来源可能是内存分配 - 如果您的线程都在大量分配和取消分配内存,那么它们最终可能会花费大量时间等待内存管理器轮到他们。 这将限制您将实现的实际并行度。

您可以使用以下代码检查在 paralel 中执行的方法数:

using System;
using System.Threading;
using System.Threading.Tasks;
namespace ParallelMethodsCounter
{
    class Program
    {
        public static int _parallelCounter = 0;
        public static int _maxParallelExecutionsCount = 0;
        static void Main(string[] args)
        {
            Parallel.For(0, 10, i => DoSomething(i));
            Console.WriteLine("MAX PARALLEL EXECUTIONS: {0}", _maxParallelExecutionsCount);
        }
        public static void DoSomething(int par)
        {
            int currentParallelExecutionsCount = Interlocked.Increment(ref _parallelCounter);
            InterlockedExchangeIfGreaterThan(ref _maxParallelExecutionsCount, currentParallelExecutionsCount, currentParallelExecutionsCount);
            Console.WriteLine("Current parallel executions: {0}", currentParallelExecutionsCount);
            try
            {
                //Do your work here
            }
            finally
            {
                Interlocked.Decrement(ref _parallelCounter);
            }
        }
        public static bool InterlockedExchangeIfGreaterThan(ref int location, int comparison, int newValue)
        {
            int initialValue;
            do
            {
                initialValue = location;
                if (initialValue >= comparison) return false;
            }
            while (Interlocked.CompareExchange(ref location, newValue, initialValue) != initialValue);
            return true;
        }
    }
}

但请记住,Parallel.For 不会尝试并行执行太多,因为它使用线程池,并且默认情况下使用等于处理器内核数的线程数。在我的四核机器上,我通常会有 2-4 次并行执行,有时最多执行 5 次(但当然这里没有严格的限制)。

不确定我是否理解你想做什么。你可以数一数。

Parallel.For(0, 10, i => DoSomething(i));
...
private static int parallelCounter = 0;
private static object counterLockObject = new Object();
void DoSomething(int par)
{
    int myCounter;
    lock(counterLockObject)
    {
        myCounter = ++parallelCounter;
    }
    try
    {
        // you probably need something that takes more time to
        // see anything executing in parallel
        Console.WriteLine("Current number of parallel threads: {0}", myCounter);
    }
    finally
    {
        lock(counterLockObject)
        {
            parallelCounter--;
        }        
    }
}