【问题标题】:static method vs instance method, multi threading, performance静态方法与实例方法,多线程,性能
【发布时间】:2012-03-28 04:10:30
【问题描述】:

你能解释一下多线程如何访问静态方法吗?多个线程可以同时访问静态方法吗?

对我来说,如果一个方法是静态的,这将使它成为一个由所有线程共享的单一资源,这似乎是合乎逻辑的。因此,一次只有一个线程能够使用它。我创建了一个控制台应用程序来测试它。但是从我的测试结果来看,我的假设似乎是不正确的。

在我的测试中,构造了许多 Worker 对象。每个Worker 都有许多密码和密钥。每个Worker 都有一个实例方法,用它的密钥散列它的密码。还有一个静态方法具有完全相同的实现,唯一的区别是它是静态的。在创建了所有Worker 对象后,开始时间将写入控制台。然后引发DoInstanceWork 事件,所有Worker 对象将它们的useInstanceMethod 排队到线程池。当所有方法或所有Worker 对象完成时,它们全部完成所花费的时间从开始时间计算并写入控制台。然后将开始时间设置为当前时间并引发DoStaticWork 事件。这一次所有的Worker 对象将它们的useStaticMethod 排队到线程池中。当所有这些方法调用都完成后,再次计算所有这些方法调用完成所花费的时间并将其写入控制台。

我预计对象使用它们的实例方法所花费的时间是它们使用静态方法所花费的时间的 1/8。 1/8 因为我的机器有 4 个内核和 8 个虚拟线程。但事实并非如此。事实上,使用静态方法所花费的时间实际上要快一些。

这是怎么回事?引擎盖下发生了什么?每个线程是否都有自己的静态方法副本?

这是控制台应用程序-

using System;
using System.Collections.Generic;
using System.Security.Cryptography;
using System.Threading;

namespace bottleneckTest
{
    public delegate void workDelegate();

    class Program
    {
        static int num = 1024;
        public static DateTime start;
        static int complete = 0;
        public static event workDelegate DoInstanceWork;
        public static event workDelegate DoStaticWork;
        static bool flag = false;

        static void Main(string[] args)
        {
            List<Worker> workers = new List<Worker>();
            for( int i = 0; i < num; i++){
                workers.Add(new Worker(i, num));
            }
            start = DateTime.UtcNow;
            Console.WriteLine(start.ToString());
            DoInstanceWork();
            Console.ReadLine();
        }
        public static void Timer()
        {
            complete++;
            if (complete == num)
            {
                TimeSpan duration = DateTime.UtcNow - Program.start;
                Console.WriteLine("Duration:  {0}", duration.ToString());
                complete = 0;
                if (!flag)
                {
                    flag = true;
                    Program.start = DateTime.UtcNow;
                    DoStaticWork();
                }
            }
        }
    }
    public class Worker
    {
        int _id;
        int _num;
        KeyedHashAlgorithm hashAlgorithm;
        int keyLength;
        Random random;
        List<byte[]> _passwords;
        List<byte[]> _keys;
        List<byte[]> hashes;

        public Worker(int id, int num)
        {
            this._id = id;
            this._num = num;
            hashAlgorithm = KeyedHashAlgorithm.Create("HMACSHA256");
            keyLength = hashAlgorithm.Key.Length;
            random = new Random();
            _passwords = new List<byte[]>();
            _keys = new List<byte[]>();
            hashes = new List<byte[]>();
            for (int i = 0; i < num; i++)
            {
                byte[] key = new byte[keyLength];
                new RNGCryptoServiceProvider().GetBytes(key);
                _keys.Add(key);

                int passwordLength = random.Next(8, 20);
                byte[] password = new byte[passwordLength * 2];
                random.NextBytes(password);
                _passwords.Add(password);
            }
            Program.DoInstanceWork += new workDelegate(doInstanceWork);
            Program.DoStaticWork += new workDelegate(doStaticWork);
        } 
        public void doInstanceWork()
        {
            ThreadPool.QueueUserWorkItem(useInstanceMethod, new WorkerArgs() { num = _num, keys = _keys, passwords = _passwords });
        }
        public void doStaticWork()
        {
            ThreadPool.QueueUserWorkItem(useStaticMethod, new WorkerArgs() { num = _num, keys = _keys, passwords = _passwords });
        }
        public void useInstanceMethod(object args)
        {
            WorkerArgs workerArgs = (WorkerArgs)args;
            for (int i = 0; i < workerArgs.num; i++)
            {
                KeyedHashAlgorithm hashAlgorithm = KeyedHashAlgorithm.Create("HMACSHA256");
                hashAlgorithm.Key = workerArgs.keys[i];
                byte[] hash = hashAlgorithm.ComputeHash(workerArgs.passwords[i]);
            }
            Program.Timer();
        }
        public static void useStaticMethod(object args)
        {
            WorkerArgs workerArgs = (WorkerArgs)args;
            for (int i = 0; i < workerArgs.num; i++)
            {
                KeyedHashAlgorithm hashAlgorithm = KeyedHashAlgorithm.Create("HMACSHA256");
                hashAlgorithm.Key = workerArgs.keys[i];
                byte[] hash = hashAlgorithm.ComputeHash(workerArgs.passwords[i]);
            }
            Program.Timer();
        }
        public class WorkerArgs
        {
            public int num;
            public List<byte[]> passwords;
            public List<byte[]> keys;
        }
    }
}

【问题讨论】:

    标签: c# multithreading


    【解决方案1】:

    在所有情况下,无论是静态的还是实例的,任何线程都可以随时访问任何方法,除非您明确地阻止它。

    例如,您可以创建一个锁以确保只有单个线程可以访问给定的方法,但 C# 不会为您这样做。

    把它想象成看电视。电视不会阻止多人同时观看,只要每个观看它的人都想看同一个节目,就没有问题。您当然不希望电视只允许一个人同时观看,因为多个人可能想观看不同的节目,对吧?因此,如果人们想观看不同的节目,他们需要某种电视本身之外的机制(也许只有一个遥控器,当前观众在观看节目期间一直握着它),以确保一个人不会改变另一个人正在观看他的节目的频道。

    【讨论】:

      【解决方案2】:

      当您访问实例方法时,您是通过对象引用访问它。

      当您访问静态方法时,您是在直接访问它。

      所以静态方法要快一点。

      【讨论】:

        【解决方案3】:

        方法就是代码——线程同时访问该代码没有问题,因为代码不会通过运行来修改;这是一个只读资源(除了抖动)。在多线程情况下需要小心处理的是并发访问数据(更具体地说,当可以修改该数据时)。方法是static还是实例方法与是否需要以某种方式进行序列化以使其成为线程安全无关。

        【讨论】:

        • 感谢您对方法是只读资源的解释。我想我理解的巨大差距是许多线程如何同时读取相同的资源。我想这对于基本了解计算机是相当基础的。我想我是在下意识地认为一个方法每个时钟周期只能读取一次,而我猜实际上它可以被许多线程读取多次。但是,是否可以说读取只是出现并发,因为它们都发生在一个时钟周期内?还有一个方法是如何读取的?一次性还是分段?谢谢
        【解决方案4】:

        当您实例化一个类时,您不会创建代码的副本。您有一个指向类定义的指针,并且通过它访问代码。因此,实例方法的访问方式比静态方法更合理

        【讨论】:

          【解决方案5】:

          C# 方法是“可重入的”(与大多数语言一样;我最后一次听说真正的不可重入代码是 DOS 例程)每个线程都有自己的调用堆栈,当调用一个方法时,该方法的调用堆栈线程更新为有空间用于返回地址、调用参数、返回值、局部值等。

          假设 Thread1 和 Thread2 同时调用方法 M 并且 M 有一个局部 int 变量 n。 Thread1 的调用栈与 Thread2 的调用栈是分开的,所以 n 在两个不同的栈中会有两个不同的实例化。只有当 n 不是存储在堆栈中而是存储在同一个寄存器中(即在共享资源中)时,并发才会成为问题 CLR(或者它是 Windows?)小心不要让这导致问题并清理、存储和恢复切换线程时的寄存器。 (多CPU的情况下怎么做,寄存器怎么分配,锁怎么实现。这些确实是让编译器和操作系统编写者想一想的难题)

          当两个线程同时调用同一个方法时,可重入并不能证明没有坏事发生:它只证明当方法不访问和更新其他共享资源时不会发生坏事。

          【讨论】:

            猜你喜欢
            • 2012-08-30
            • 2011-03-02
            • 2011-03-26
            • 2012-11-12
            • 1970-01-01
            • 2015-06-22
            • 1970-01-01
            • 1970-01-01
            • 1970-01-01
            相关资源
            最近更新 更多