c# - 静态方法与实例方法,多线程,性能

标签 c# multithreading

能帮忙解释一下多线程是如何访问静态方法的吗?多个线程是否可以同时访问静态方法?

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

在我的测试中,构建了许多 Worker 对象。每个 Worker 都有一些密码和 key 。每个 Worker 都有一个实例方法,用它的 key 散列它的密码。还有一个静态方法具有完全相同的实现,唯一的区别是它是静态的。在创建所有 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;
        }
    }
}

最佳答案

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

关于c# - 静态方法与实例方法,多线程,性能,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/9655573/

相关文章:

c# - 为列表中的每个项目设置权重

c# - Xamarin Android - Task.Run 与 Task.Factory.StartNew 和 Thread.CurrentPrincipal

c++ - 定义线程函数时的 std::thread 表示法

java - Spring Boot 中的线程本地行为

c# - 事件未在表单中正确引发

java - 每秒将数据保存到数据库,解决数据库连接丢失或缓慢的问题

c# - 不存在控制台时写入标准输出

c# - Windows 服务如何确定其服务名称?

c# - 将 Dictionary<T> 绑定(bind)到 WPF 列表框

c - errno 是线程安全的吗?