簡體   English   中英

C# 主線程比任何其他線程更快地完成相同的操作?

[英]C# Main Thread completes same action faster than any other thread?

我在 C# 中執行了一些進程間(應用程序間)通信代碼。 並注意到如果我在主線程上運行測試,測試完成速度會快 20~30%。 無論動作是作為函數/方法調用還是我們使用主線程的 SynchronizationContext 和 Post 動作。 所以我開始檢查主線程的行為,並注意到主線程從不休眠。 我收集主線程負責 UI 響應並需要運行消息泵(也許答案是消息泵,並且 OS 調度程序為此類線程提供了更多時間片?)。 我正在尋找更好地理解這背后的原因。

我已經編寫了一些測試代碼,運行它超過 dosen 次,每次測試顯示主線程在其他線程之后開始執行進程,並且該主線程設法從其他線程更快地完成工作。 這是測試的代碼。

using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading;
using System.Diagnostics;

namespace CSMainThread
{
  static class MainThreadTest
  {
    static ManualResetEvent eventRace = new ManualResetEvent(false);

    static object SyncObject = new object();
    static int prizeStart = 0;
    static int prizeEnd = 0;

    static long[] slices = new long[2];
    static int[] countsStart = new int[2];
    static int[] countsEnd = new int[2];

    static ManualResetEvent[] eventPrepStart = new ManualResetEvent[2] { new ManualResetEvent(false), new ManualResetEvent(false) };
    
    static Thread tOther;
    static Thread tSync;

    public static void Test()
    {
      DateTime tt = DateTime.Now;
      for (int i = 0; i < 100; i++)
      {
        eventRace.Reset();
        prizeStart = 1;
        prizeEnd = 1;
        tOther  = new Thread(new ParameterizedThreadStart(ThreadProc));
        tSync = new Thread(new ParameterizedThreadStart(ThreadProcSync));        
        // start starter thread, before main thread
        tSync.Start(); 
        // start other thread
        tOther.Start(1);
        // go with main thread
        ThreadProc(0);
        // wait other thread end
        tOther.Join();        
      }
      Debug.Print("Total test time {0} ms", (DateTime.Now - tt).TotalMilliseconds);
      Debug.Print(new string('-', 60));
      Debug.Print("Thread  | Start |   End | Slices");
      Debug.Print(new string('-', 60));
      Debug.Print(" Main   | {0,5} | {1,5} | {2}", countsStart[0], countsEnd[0], slices[0]);
      Debug.Print(" Other  | {0,5} | {1,5} | {2}", countsStart[1], countsEnd[1], slices[1]);
      Debug.Print(new string('-', 60));
    }

    static void ThreadProcSync(object threadData)
    {
      // wait both threads start
      while (!eventPrepStart[0].WaitOne(0) || !eventPrepStart[1].WaitOne(0))
        Thread.Yield();
  
      // NOTE criteria : Thread.CurrentThread.ThreadState == System.Threading.ThreadState.WaitSleepJoin is never satisfied !!!
      //                 MainThread never enters wait state, and this is Dead Locking sync thread !!!
      //
      // ensure wait is on both threads
      //while (!(Thread.CurrentThread.ThreadState == System.Threading.ThreadState.WaitSleepJoin && tOther.ThreadState == System.Threading.ThreadState.WaitSleepJoin))
      //  Thread.Yield();      
      while (!(tOther.ThreadState == System.Threading.ThreadState.WaitSleepJoin))
        Thread.Yield();      

      // run the race
      eventRace.Set();      
    }

    static void ThreadProc(object threadData)
    {
      double x = 0;
      int i;
      DateTime tt;
      int index = (Int32)threadData;
      eventPrepStart[index].Set();
      eventRace.WaitOne();

      // make sure no wait happens out of order
      Thread.MemoryBarrier();
      
      lock (SyncObject)
      {
        tt = DateTime.Now;
        // the one that gets here first takes the prize
        countsStart[index] += prizeStart;
        prizeStart = 0;        
      }      
      for (i = 0; i < 1000000; i++)
      {
        x += Math.Cos(i) * Math.Sin(i);
      }
      lock (SyncObject)
      {
        slices[index] += (DateTime.Now - tt).Ticks;
        // the one that gets here first takes the prize
        countsEnd[index] += prizeEnd;
        prizeEnd = 0;
      }
      
      // get ready for next run
      eventPrepStart[index].Reset();
      
    }

  }
}

也許它需要更多的工作(HighPerfomanceCounters ...),但總的來說給我們一些圖片

這是我得到的結果,在大多數情況下,其他線程首先運行並獲得開始獎,但在大約 50% 的情況下,主線程趕上其他線程獲得結束獎並贏得比賽。

Total test time 12202.698 ms
------------------------------------------------------------
Thread  | Start |   End | Slices
------------------------------------------------------------
Main   |     3 |    62 | 94465397
Other  |    97 |    38 | 99685701
------------------------------------------------------------

有誰知道真正發生了什么,為什么主線程完成得更快?

編輯:只為 FOR(Cos*Sin) 使用了一些高性能計數器分析器,它返回了同樣的東西。

Total test time 11804.6752 ms
------------------------------------------------------------
Thread  | Start |   End | Slices
------------------------------------------------------------
 Main   |     2 |    54 | 91425226
 Other  |    98 |    46 | 98455625
------------------------------------------------------------
HPC[1001]::ShowStatistic() called from : [JITTrack,JITOpt] CSMainThread.MainThreadTest::Test()
┌─────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────┐
│ meter : sumtime  / cnt ~ average  | times * time.min ~ time.max : max-min  ( max-avrg , avrg-min ) over time improving  │
├─────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────┤
│ Main  : 8.536089 / 100 ~ 0.085361 |     3 * 0.073004 ~ 0.219276 : 0.146273 ( 0.133915 , 0.012357 ) ▫▫▫▫▫▫▫▫▫▫▫▫▫▫▫▫▫▫▫▫ │
│ Other : 9.560208 / 100 ~ 0.095602 |     2 * 0.072965 ~ 0.173809 : 0.100844 ( 0.078207 , 0.022637 ) ▫▫▫▫▫▫▫▫▫▫▫▫▫▫▫▫▫▫▫▫ │
└─────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────┘

從未見過這樣的事情:

請嘗試以下方法:

  • 更改線程的啟動順序,我認為這可能會影響到每個受影響的資源。

  • 禁用正在運行的計算機上的所有服務
    程序。

我認為這與主線程/“普通線程”無關,但與您的 SO 處理每個線程的資源屬性和並發的方式有關(在這種情況下。

  1. 我對您的代碼進行了一些重構,以使裁判和線程之間的同步更簡單一些。
  2. 不幸的是,我沒有注意到你做了什么。 我實際上得到的是或多或少的均勻分布。 不過,我只做了少數測試。 此外,我沒有關閉計算機上可能會影響結果的任何功能,因此我的 Spotify 已打開,並且電子郵件正在進來。比較這篇文章底部的結果

我的代碼

using System;
using System.Diagnostics;
using System.Threading;

namespace CSMainThread
{
    static class MainThreadTest
    {
        static object SyncObject = new object();
        static int prizeStart = 0;
        static int prizeEnd = 0;

        static long[] slices = new long[2];
        static int[] countsStart = new int[2];
        static int[] countsEnd = new int[2];

        public static void Main()
        {
            using var eventRace = new ManualResetEvent(false);

            DateTime tt = DateTime.Now;
            for (int i = 0; i < 100; i++)
            {
                Debug.Print($"Run #{i}");
                eventRace.Reset();
                using var competitors = new CountdownEvent(2);
                prizeStart = 1;
                prizeEnd = 1;
                // start starter thread, before main thread
                // start other thread
                var tOther = new Thread(ThreadProc);
                var tSync = new Thread(RefereeThread);
                tSync.Start((sync: eventRace, referee: competitors));
                tOther.Start((index: 1, sync: eventRace, referee: competitors));
                // go with main thread
                ThreadProc((index: 0, sync: eventRace, referee: competitors));
                // wait other thread end
                tOther.Join();
            }
            Console.WriteLine("Total test time {0} ms", (DateTime.Now - tt).TotalMilliseconds);
            Console.WriteLine(new string('-', 60));
            Console.WriteLine("Thread  | Start |   End | Slices");
            Console.WriteLine(new string('-', 60));
            Console.WriteLine(" Main   | {0,5} | {1,5} | {2}", countsStart[0], countsEnd[0], slices[0]);
            Console.WriteLine(" Other  | {0,5} | {1,5} | {2}", countsStart[1], countsEnd[1], slices[1]);
            Console.WriteLine(new string('-', 60));
        }

        static void RefereeThread(object data)
        {
            var (sync, referee) = ((
                ManualResetEvent sync,
                CountdownEvent referee))data;

            // Wait for all the threads we're checking to get to their `referee.Signal`.
            referee.Wait();
            // Give threads some time to fall into wait procedure.
            Thread.Sleep(100);
            // start the race
            sync.Set();
        }

        static void ThreadProc(object data)
        {
            var (index, sync, referee) = ((
                int index,
                ManualResetEvent sync,
                CountdownEvent referee))data;
            double x = 0;
            int i;
            DateTime tt;

            // make sure no wait happens out of order
            referee.Signal();
            sync.WaitOne();

            lock (SyncObject)
            {
                tt = DateTime.Now;
                // the one that gets here first takes the prize
                countsStart[index] += prizeStart;
                prizeStart = 0;
            }
            for (i = 0; i < 1000000; i++)
            {
                x += Math.Cos(i) * Math.Sin(i);
            }
            lock (SyncObject)
            {
                slices[index] += (DateTime.Now - tt).Ticks;
                // the one that gets here first takes the prize
                countsEnd[index] += prizeEnd;
                prizeEnd = 0;
            }
        }
    }
}

結果

在此處輸入圖像描述

暫無
暫無

聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.

 
粵ICP備18138465號  © 2020-2024 STACKOOM.COM