这里的问题不止一件事,振作起来……
Start 创建一个Stopwatch sw,但您在循环的每次迭代中调用sw.Start。只启动一次。
-
如果i == ThreadsNumber - 1 的计算结果为true,则让Thread 成为垃圾。我不明白为什么......
(MaxNumber / ThreadsNumber) * (i + 1) WHEN i == ThreadsNumber - 1
=
(MaxNumber / ThreadsNumber) * (ThreadsNumber - 1 + 1)
=
(MaxNumber / ThreadsNumber) * (ThreadsNumber)
=
MaxNumber
您有舍入问题吗?改写如下:
((i + 1) * MaxNumber) / ThreadsNumber
通过最后除,可以避免舍入问题。
您正在等待线程 while (threads.All(t => t.IsAlive));。您也可以使用Thread.Join 或更好,让线程在完成时通知您。
lambda 中的范围在i 上有一个闭包。你需要小心C# - For loop and the lambda expressions。
List<T> 不是线程安全的。我建议使用一个简单的数组(毕竟你知道线程的数量)并告诉每个线程只存储与它们对应的位置。
您尚未考虑如果在第一次调用结束之前对Start 进行第二次调用会发生什么。
所以,我们将有一个输出数组:
var output = new long[ThreadsNumber];
还有一个用于线程:
var threads = new Thread[ThreadsNumber];
嗯,就像我们应该创建一个类一样。
我们会有秒表:
var sw = new Stopwatch();
让我们开始一次:
sw.Start();
现在是 for 来创建线程:
for (var i = 0; i < ThreadsNumber; i++)
{
// ...
}
拥有i 的副本以防止出现问题:
for (var i = 0; i < ThreadsNumber; i++)
{
var index = i;
// ...
}
计算当前线程的范围:
for (var i = 0; i < ThreadsNumber; i++)
{
var index = i;
var start = 1 + (i * MaxNumber) / ThreadsNumber;
var end = ((i + 1) * MaxNumber) / ThreadsNumber;
// ...
}
我们需要写Sum,这样我们就可以将输出存储在数组中:
private void Sum(long startNumber, long endNumber, int index)
{
long result = 0;
for (long i = startNumber; i <= endnumber; i++)
{
result += i;
}
output[index] = result;
}
嗯...等等,还有更好的方法...
private static void Sum(long startNumber, long endNumber, out long output)
{
long result = 0;
for (long i = startNumber; i <= endNumber; i++)
{
result += i;
}
output = result;
}
嗯...不,我们可以做得更好...
private static long Sum(long startNumber, long endNumber)
{
long result = 0;
for (long i = startNumber; i <= endNumber; i++)
{
result += i;
}
return result;
}
创建Thread
for (var i = 0; i < ThreadsNumber; i++)
{
var index = i;
var start = 1 + (i * MaxNumber) / ThreadsNumber;
var end = ((i + 1) * MaxNumber) / ThreadsNumber;
threads[i] = new Thread(() => output[index] = Sum(start, end));
// ...
}
然后启动Thread:
for (var i = 0; i < ThreadsNumber; i++)
{
var index = i;
var start = 1 + (i * MaxNumber) / ThreadsNumber;
var end = ((i + 1) * MaxNumber) / ThreadsNumber;
threads[i] = new Thread(() => {output[index] = Sum(start, end);});
threads[i].Start();
}
我们真的要等这些吗?
想想,想想……
我们跟踪有多少线程处于待处理状态...当它们都完成时,我们调用事件(并停止秒表)。
var pendingThreads = ThreadsNumber;
// ...
for (var i = 0; i < ThreadsNumber; i++)
{
// ...
threads[i] = new Thread
(
() =>
{
output[index] = Sum(start, end);
if (Interlocked.Decrement(ref pendingThreads) == 0)
{
sw.Stop();
finished?.Invoke
(
this,
new CalcFinishedEventArgs()
{
Result = output.Sum(),
Time = sw.ElapsedMilliseconds
}
);
}
}
);
// ...
}
让我们一起来:
void Main()
{
var pc = new ParallelCalc(20, 5);
pc.Finished += (sender, args) =>
{
Console.WriteLine(args);
};
pc.Start();
}
public class CalcFinishedEventArgs : EventArgs
{
public long Result {get; set;}
public long Time {get; set;}
}
public class ParallelCalc
{
public long MaxNumber { get; set; }
public int ThreadsNumber { get; set; }
public event EventHandler<CalcFinishedEventArgs> Finished;
public ParallelCalc(long MaxNumber, int ThreadsNumber)
{
this.MaxNumber = MaxNumber;
this.ThreadsNumber = ThreadsNumber;
}
public void Start()
{
var output = new long[ThreadsNumber];
var threads = new Thread[ThreadsNumber];
var pendingThreads = ThreadsNumber;
var sw = new Stopwatch();
sw.Start();
for (var i = 0; i < ThreadsNumber; i++)
{
var index = i;
var start = 1 + (i * MaxNumber) / ThreadsNumber;
var end = ((i + 1) * MaxNumber) / ThreadsNumber;
threads[i] = new Thread
(
() =>
{
output[index] = Sum(start, end);
if (Interlocked.Decrement(ref pendingThreads) == 0)
{
sw.Stop();
Finished?.Invoke
(
this,
new CalcFinishedEventArgs()
{
Result = output.Sum(),
Time = sw.ElapsedMilliseconds
}
);
}
}
);
threads[i].Start();
}
}
private static long Sum(long startNumber, long endNumber)
{
long result = 0;
for (long i = startNumber; i <= endNumber; i++)
{
result += i;
}
return result;
}
}
输出:
Result
210
Time
0
太快了……让我输入:
var pc = new ParallelCalc(2000000000, 5);
pc.Finished += (sender, args) =>
{
Console.WriteLine(args);
};
pc.Start();
输出:
Result
2000000001000000000
Time
773
And that is correct.
是的,这段代码处理了多次调用Start 的情况。请注意,它每次都会为输出创建一个新数组和一个新的线程数组。这样,它就不会自己绊倒。
我让你处理错误。提示:MaxNumber / ThreadsNumber -> 除以 0,(i + 1) * MaxNumber -> 溢出,更不用说output.Sum() -> 溢出。