[英]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 處理每個線程的資源屬性和並發的方式有關(在這種情況下。
我的代碼
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.