我正在實施一些多線程單元測試,並提出了一個難以確保兩個作業實際並行執行的問題 - 一個總是先啓動另一個。讓我們考慮一下我的初步實施測試場景來演示行爲:如何確保任務同時執行?
static void Main(string[] args)
{
var repeats = 1000000;
var firstWinCount = 0;
var secondWinCount = 0;
int x = 0;
long time1 = 0;
long time2 = 0;
long totalTimeDiff = 0;
var sw = new Stopwatch();
sw.Start();
for (int i = 0; i < repeats; i++)
{
x = 0;
var task1 = new Task(() =>
{
Interlocked.CompareExchange(ref x, 1, 0);
time1 = sw.ElapsedMilliseconds;
});
var task2 = new Task(() =>
{
Interlocked.CompareExchange(ref x, 2, 0);
time2 = sw.ElapsedMilliseconds;
});
task1.Start();
task2.Start();
Task.WaitAll(task1, task2);
totalTimeDiff += Math.Abs(time1 - time2);
if (x == 1)
{
firstWinCount++;
}
else
{
if (x == 2)
{
secondWinCount++;
}
}
}
Console.WriteLine("First win count: {0}, percentage: {1}", firstWinCount, firstWinCount/(double)repeats * 100);
Console.WriteLine("Second win count: {0}, percentage: {1}", secondWinCount, secondWinCount/(double)repeats * 100);
Console.WriteLine("Avg sync diff: {0}ns", totalTimeDiff * 1000000/repeats);
}
輸出是:
First win count: 950538, percentage: 95,0538
Second win count: 49462, percentage: 4,9462
Avg sync diff: 1012ns
我們可以看到,大部分的時間第一個任務開始執行早些時候然後第二個,因爲它得到到線程池第一:
task1.Start();
task2.Start();
由於線程池是在某種程度上任務安排非常難以預測,也絕對沒有保證日首先任務將不會完成,直到第二個任務開始。所以很難確保我們正在測試多線程場景。
令人驚訝的是,我在網上找不到類似的問題。
我自己的考慮和想法AutoResetEvents,鎖和互鎖同步建設導致任務同步的以下解決方案:
int sync = 0;
var task1 = new Task(() =>
{
Interlocked.Increment(ref sync);
while (Interlocked.CompareExchange(ref sync, 3, 2) != 2) ;
Interlocked.CompareExchange(ref x, 1, 0);
time1 = sw.ElapsedMilliseconds;
});
var task2 = new Task(() =>
{
while (Interlocked.CompareExchange(ref sync, 2, 1) != 1) ;
Interlocked.CompareExchange(ref x, 2, 0);
time2 = sw.ElapsedMilliseconds;
});
基本上這個想法確保兩個線程不會被阻止(所以很可能有處理器時間),同時等待其他任務開始處理。其結果是,我設法減少同步時間差〜1000納秒至〜130納秒和大大增加的短運行的任務的概率在並行執行:
First win count: 23182, percentage: 2,3182
Second win count: 976818, percentage: 97,6818
Avg sync diff: 128ns
剩餘缺點是任務的那排序是仍然相當明確:第一個任務總是等待第二個完成,第二個,一旦第一個知道第一個等待它,不再等待並開始執行它的工作。所以第二份工作可能首先開始。據我所知,由於[相對較少]的線程切換,exclusins(2.3%)是可能的。我可以用隨機同步順序來解決它,但這是另一個複雜因素。
我想知道我是否在重新發明輪子,是否有更好的方法來最大限度地提高兩個任務同時執行的概率,並且每個任務的啓動稍微早一些。
PS:據我所知,多線程情景通常是遠慢然後100納秒(在同步施工慢是至少1000倍任何線程切換或塊),所以該延遲同步並不重要在大多數案例。但是在測試非阻塞的高性能代碼時,這可能是至關重要的。
有*無*你可以做,以確保他們在同一時間運行。計算機可能甚至沒有多個CPU,所以不能一次運行兩個線程,或者可能有其他程序佔用了太多的CPU資源,以至於你只能一次運行一個CPU ,或者操作系統可能只是決定永遠不會爲你的應用程序安排多個線程,因爲它可以自由地安排線程,但是它是想要的。 – Servy
因此,您正在測試.NET任務實施?我認爲MS的一些人已經完成了這種測試,並且可以確定兩項任務並行運行(或至少以舊式的循環方式)。 – fharreau
@fharreau這些任務可以並行運行。做到這一點很容易。 *要求*它們並行運行將會*不可能*,因爲它依賴於操作系統和硬件,而且現在大多數PC都沒有這樣的框架來支持*這樣的東西。 – Servy