我正在实施一些多线程单元测试,并提出了一个难以确保两个作业实际并行执行的问题 - 一个总是先启动另一个。让我们考虑一下我的初步实施测试场景来演示行为:如何确保任务同时执行?
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