【问题标题】:timing a mutex,unsynchronized threads, and a semaphore in C#在 C# 中计时互斥锁、非同步线程和信号量
【发布时间】:2019-12-12 03:52:12
【问题描述】:

我有一个任务,我的任务是用 c# 编写一个程序来测试 5 个不同的功能。我似乎遇到了问题,因为这些测试的时间输出似乎是错误的(即它们总是返回 0ms)。此外,我的未同步线程总是返回 5000 的完整值,而他们不应该一直这样做,我在测试#2 中做错了什么,我是否在错误的区域停止了计时器?在这一点上,线程有点让我心烦意乱,我想对此事进行一些澄清......下面是规范和我的代码......

测试#1:将是值 5000 的增量(基本)。

测试#2:主线程将创建 10 个线程,每个线程将共享整数递增 500 次。您不会使用任何同步来保护共享整数的更新。对共享整数的非同步访问很可能会导致它的最终值不正确(

测试#3:主线程将创建 10 个线程,每个线程将共享整数递增 500 次。您将使用互斥锁保护共享整数的更新。

测试#4:主线程将创建 10 个线程,每个线程将共享整数递增 500 次。您将使用 Interlocked 类的 Increment 方法更新共享整数。

测试#5:主线程将创建 10 个线程,每个线程将共享整数递增 500 次。您将使用信号量保护共享整数的更新。

  • 每个测试都将使用 Stopwatch 类进行计时,并且测试所用的总时间将被写入控制台。

  • 使用 Thread.Sleep() 模拟随机数量的处理时间,并假设当时正在使用共享整数。

  • 随机时间应该是 0 到 10ms 之间的随机值。

    using System;
    using System.Collections.Generic;
    using System.Diagnostics;
    using System.Linq;
    using System.Threading;
    
    namespace Lab3
    {
        class MainClass
        {
        public static int sharedVal = 0;
        public static int y = 1;
        static Stopwatch timer = new Stopwatch();
        private static Mutex mutex = new Mutex();
        private static Semaphore semaphore = new Semaphore(1, 1);
    
    
        public static void Main(string[] args)
        {
            Console.WriteLine("***************** Lab 3 Thread/Synchronization Testing *****************" + "\n");
            Console.WriteLine("Starting Test 1 (no Threads)...");
            test1();
            Console.WriteLine("***************** Lab 3 Thread/Synchronization Testing *****************" + "\n");
            Console.WriteLine("Starting Test 2 (Threads without any synchronization)...");
            test2(10);
            Console.WriteLine("***************** Lab 3 Thread/Synchronization Testing *****************" + "\n");
            Console.WriteLine("Starting Test 3 (Threads with a mutex)...");
            test3(10);
            Console.WriteLine("***************** Lab 3 Thread/Synchronization Testing *****************" + "\n");
            Console.WriteLine("Starting Test 4 (Interlocked Methods)...");
            test4(10);
            Console.WriteLine("***************** Lab 3 Thread/Synchronization Testing *****************" + "\n");
            Console.WriteLine("Starting Test 5 (Threads with a semaphore)...");
            test5(10);
    
        }
        /*****************************************************************************/
        public static void test1()
        {
            timer.Reset();
            timer.Start();
            intChanger(ref sharedVal, 5000,0);
            Console.WriteLine("Test Complete");
            timer.Stop();
            Console.WriteLine("Shared Value: {0}, Total time: {1}ms ", sharedVal, timer.ElapsedMilliseconds);
    
            clearSharedVal(ref sharedVal);
        }
        /*****************************************************************************/
        public static void test2(int numOfThreads)
        {
            timer.Reset();
            timer.Start();
            threadCreate(numOfThreads);
            Console.WriteLine("Test Complete");
            Console.WriteLine("Shared Value: {0}, Total time: {1}ms ", sharedVal, timer.ElapsedMilliseconds);
            clearSharedVal(ref sharedVal);
            y++;
        }
        /*****************************************************************************/
        public static void test3(int numOfThreads)
        {
            timer.Reset();
            timer.Start();
            threadCreate(numOfThreads);
            Console.WriteLine("Test Complete");
            Console.WriteLine("Shared Value: {0}, Total time: {1}ms ", sharedVal, timer.ElapsedMilliseconds);
            clearSharedVal(ref sharedVal);
            y++;
        }
        /*****************************************************************************/
        public static void test4(int numOfThreads)
        {
            timer.Reset();
            timer.Start();
            threadCreate(numOfThreads);
            Console.WriteLine("Test Complete");
            Console.WriteLine("Shared Value: {0}, Total time: {1}ms ", sharedVal, timer.ElapsedMilliseconds);
            clearSharedVal(ref sharedVal);
            y++;
        }
        /*****************************************************************************/
        public static void test5(int numOfThreads)
        {
            timer.Reset();
            timer.Start();
            threadCreate(numOfThreads);
            Console.WriteLine("Test Complete");
            Console.WriteLine("Shared Value: {0}, Total time: {1}ms ",sharedVal, timer.ElapsedMilliseconds);
            clearSharedVal(ref sharedVal);
        }
        /*****************************************************************************/
        public static void threadCreate(int n)
        {
            Thread[] threadArr = new Thread[n];
            for (int i = 0; i < n; i++)
            {
                threadArr[i] = new Thread(new ThreadStart(WorkThreadFunction));
                threadArr[i].Start();              
            }
            //join for loop
            for (int i = 0; i < n; i++)
            {
                threadArr[i].Join();
            }
            Console.WriteLine("All Threads have been joined");
            timer.Stop();
        }
        /******************************************************************************/
        public static void WorkThreadFunction()
        {
            switch (y) { 
                case 1:
                    intChanger(ref sharedVal, 500, 1);
                    break;
                case 2:
                    intChanger(ref sharedVal, 500, 2);
                    break;
                case 3:
                    intChanger(ref sharedVal, 500, 3);
                    break;
                case 4:
                    intChanger(ref sharedVal, 500, 4);
                    break;
            }
        }
    
        /*****************************************************************************/
      public static void intChanger(ref int s, int n , int flag)
      {
        /*******************************************************************************
        *                            Flag Explanation                                  *
        ********************************************************************************
        *    int flag = 0 --> unsynchronized single thread adding to shared value      *
        *    int flag = 1 --> unsynchronized threads adding to shared value            *
        *    int flag = 2 --> synchronized using a mutex                               *
        *    int flag = 3 --> synchronized using Increment method of Interlocked class *
        *    int flag = 4 --> synchronized using semaphore                             *
        ********************************************************************************/
    
            Random rand = new Random();
    
            switch (flag)
            {
                case 0:
                    for (int i = 0; i < n; i++)
                    {
                        s++;
                    }
    
                    break;
    
                case 1:
                    for (int i = 0; i < n; i++)
                    {
                        s++;
                    }
                    timer.Stop();
                    break;
    
                case 2:
                    for (int i = 0; i < n; i++)
                    {
                        mutex.WaitOne();
                        Thread.Sleep(rand.Next(0, 10));
                        s++;
                        mutex.ReleaseMutex();
                    }
                    break;
    
                case 3:
                    for (int i = 0; i < n; i++)
                    {
                        //Thread.Sleep(rand.Next(0, 10) * 1000);
                        Interlocked.Increment(ref s);
                    }
                    timer.Stop();
                    break;
    
                case 4:
                    for (int i = 0; i < n; i++)
                    {
                        semaphore.WaitOne();
                        Thread.Sleep(rand.Next(0, 10));
                        s++;
                        semaphore.Release();
                    }
                    break;
            }
        }
            /*****************************************************************************/
            public static void clearSharedVal(ref int n)
        {
            n = 0;
        }
    }
    

    }

【问题讨论】:

  • 对于 2:线程将完成其循环,在您再次调用 thread[i].Start() 之前,将您的 n 增加到 1_000_000,它将以小于 10m 的东西结束。
  • 请注意,您也在测量线程启动时间和睡眠时间。我不确定你的目标是什么,但如果你想测量锁定时间,那么你应该记住这一点。线程启动时间最长30ms,你的循环大概是0.0001ms
  • 嗯,我想测量整个测试从开始到停止的时间长度。

标签: c# multithreading parallel-processing mutex semaphore


【解决方案1】:

嗯,对于现代 PC 来说,5000 增量是一项非常简单的任务,所以我预计 0 毫秒。尝试增加一些操作,或者比较 ElapsedTicks 而不是 ElapsedMilliseconds。 关于#2。创建新线程是一项繁重的任务,而工作本身是一项轻量级的任务。因此,可能到下一个线程准备好运行时,前一个线程已经完成。它们是串行运行的,而不是并行运行的。您可以再次增加操作次数,并使用threadpool 方法运行作业。

编辑:

请注意,Thread.Sleep 方法在极低延迟(低于 15 到 20 毫秒)的情况下无法正常工作:If dwMilliseconds is less than the resolution of the system clock, the thread may sleep for less than the specified length of time

【讨论】:

  • 是的,使用 3GHz 处理器,为简单起见,假设是标量处理器。递增,if 和 goto 将使用大约 3 个时钟周期,称为 10。乘以 5000,得到 50 000 个时钟。除以 3GHz,得到 10us 或 0.01ms。
猜你喜欢
  • 2014-05-02
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2023-03-09
  • 1970-01-01
  • 2020-12-07
  • 1970-01-01
相关资源
最近更新 更多