我的任务是,要在某个范围内总结数字,以实现我必须使用线程来分开计算。我将数字划分为零件,并为每个部分使用了一个线程。
public class ParallelCalc
{
public long resultLong;
private Thread[] threads;
private List<long> list = new List<long>();
public long MaxNumber { get; set; }
public int ThreadsNumber { get; set; }
public event CalcFinishedEventHandler finished;
public ParallelCalc(long MaxNumber, int ThreadsNumber)
{
this.MaxNumber = MaxNumber;
this.ThreadsNumber = ThreadsNumber;
this.threads = new Thread[ThreadsNumber];
}
public void Start()
{
Stopwatch sw = new Stopwatch();
for (int i = 0; i < ThreadsNumber; i++)
{
threads[i] = new Thread(() => Sum(((MaxNumber / ThreadsNumber) * i) + 1,
MaxNumber / ThreadsNumber * (i + 1)));
if (i == ThreadsNumber - 1)
{
threads[i] = new Thread(() => Sum(((MaxNumber / ThreadsNumber) * i) + 1,
MaxNumber));
}
sw.Start();
threads[i].Start();
}
while (threads.All(t => t.IsAlive));
sw.Stop();
finished?.Invoke(this,
new CalcFinishedEventArgs()
{
Result = list.Sum(),
Time = sw.ElapsedMilliseconds
});
}
private void Sum(long startNumber, long endnumber)
{
long result = 0;
for (long i = startNumber; i <= endnumber; i++)
{
result += i;
}
list.Add(result);
}
}
结果必须是数字的总和,但是由于列表中的线程异步分配,这是不正确的。请指示错误。
这里有多个错误
-
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
或更高的方法,让线程在完成后通知您。 -
lambdas中的范围在
i
上闭合。您需要小心C# - 循环和lambda表达式。 -
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
}
);
}
}
);
// ...
}
让我们将其全部togheter:
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
那是正确的。
是的,此代码多次调用Start
的情况。请注意,它每次都为输出创建一个新数组和一个新的线程数组。这样,它就不会自行绊倒。
我让您处理错误。提示: MaxNumber / ThreadsNumber
->除以0,而 (i + 1) * MaxNumber
->溢出,更不用说 output.Sum()
->溢出。