文章有bug.....由于Stopwatch只能监视主线程 所以其实Task那部分耗时完全没有计算进去(可以尝试在排序之后马上foreach输出一下数组,会报运行时异常,因为foreach在迭代时候是不允许对容器进行操作的而此时递归产生的Task还在对容器进行排序)
正在尝试弄一个可以监视多线程的Stopwatch
昨天研究了下利用不同容器实现快排算法(list<t>+lambda, list<int>, int[])之间的速度差异,今天在看.net 4.0的时候熟悉了下4.0下提供的牛逼哄哄的Task类尝试进行并行的快速排序算法(快排本质上是分治法,是可以并行计算的 这是理论基础 不能什么情况都套Task),结果可谓相当惊人
同样的 贴上最原始的快排代码 这里用list<t>+lambda实现
public static void Sort<T>(List<T> arrayForSort, int startIndex, int endIndex, Func<T, T, bool> CompareMethod)
{
if (startIndex >= endIndex)
{
return;
}
int pointer = startIndex;
int end = endIndex;
for (int i = pointer + 1; i <= end; i++)
{
if (CompareMethod(arrayForSort[pointer], arrayForSort[i]))//把比当前锚点数小的数全部移到锚点数左边
{
var temp = arrayForSort[pointer];
arrayForSort[pointer] = arrayForSort[i];
arrayForSort[i] = temp;
pointer = i;
}
else//若当前比较的数比锚点数大 则将其与最后面的数对调
{
var temp = arrayForSort[end];
arrayForSort[end] = arrayForSort[i];
arrayForSort[i] = temp;
--i;
--end;
}
}
Sort<T>(arrayForSort, startIndex, pointer, CompareMethod);
Sort<T>(arrayForSort, pointer + 1, endIndex, CompareMethod);
}
然后是Task的并行实现 不得不夸一下微软真是太为程序员考虑 代码基本没有大的变动 只在递归调用的地方改成由Task调用就可以了
public static void SortUsingTask<T>(List<T> arrayForSort, int startIndex, int endIndex, Func<T, T, bool> CompareMethod)
{
if (startIndex >= endIndex)
{
return;
}
int pointer = startIndex;
int end = endIndex;
for (int i = pointer + 1; i <= end; i++)
{
if (CompareMethod(arrayForSort[pointer], arrayForSort[i]))//把比当前锚点数小的数全部移到锚点数左边
{
var temp = arrayForSort[pointer];
arrayForSort[pointer] = arrayForSort[i];
arrayForSort[i] = temp;
pointer = i;
}
else//若当前比较的数比锚点数大 则将其与最后面的数对调
{
var temp = arrayForSort[end];
arrayForSort[end] = arrayForSort[i];
arrayForSort[i] = temp;
--i;
--end;
}
}
new Task(() => SortUsingTask<T>(arrayForSort, startIndex, pointer, CompareMethod)).Start();
new Task(() => SortUsingTask<T>(arrayForSort, pointer + 1, endIndex, CompareMethod)).Start();
}
调用代码相当简单 如下
class Program
{
static void Main(string[] args)
{
Random rdm = new Random();
List<int> testList = new List<int>();
List<int> testList2 = new List<int>();
List<int> testList3 = new List<int>();
for (int i = 0; i < 100000; i++)
{
var number = rdm.Next(1000000);
testList.Insert(i, number);
testList2.Insert(i, number);
testList3.Insert(i, number);
}
var watch1 = new Stopwatch();
watch1.Start();
QuickSortMain.Sort<int>(testList, 0, testList.Count-1, (x, y) => { return x > y; });
watch1.Stop();
var customSort = watch1.ElapsedMilliseconds;
watch1.Reset();
watch1.Start();
QuickSortMain.SortUsingTask<int>(testList2, 0, testList2.Count - 1, (x, y) => { return x > y; });
watch1.Stop();
var customSort2 = watch1.ElapsedMilliseconds;
watch1.Reset();
watch1.Start();
testList3.Sort();
watch1.Stop();
var systemSort = watch1.ElapsedMilliseconds;
//foreach (var item in testList2)//验证并行计算的快排结果是否正确
//{
// Console.WriteLine(item.ToString());
//}
Console.WriteLine("无并行计算版本 Custom1 Sort:" + customSort);//快排,比较方法用了lambda,容器为list<int>
Console.WriteLine("并行计算版本 Custom2 Sort:" + customSort2);//快排,比较方法用了lambda,容器为list<int>
Console.WriteLine("C#自带排序方法 System Sort:" + systemSort);//系统自带排序
Console.ReadLine();
}
}
运行结果如下图
可以看到我自己写的快速排序算法在没有使用Task进行并行计算前 速度直接比.net list自带的排序慢了一个数量级,但是用了Task进行并行计算后 自己比list自带排序还要快(窃喜 有种读书时候超越年级第一的感觉)
Task的好处,首先就是Task利用线程池 这点必须和一般的Thread区分开(根本无法想象这边不用线程池而直接用thread开几万个线程会卡到什么程度)
其次这种并行计算必须是在特定条件下才可以使用 这里在这里由于快速排序是分治的思想 所以才可以用Task来进行并行计算
最后。。。.net list自带的sort也不过如此嘛。。。。