我的任务是,总结一些范围内的数字,实现我必须使用线程来分离计算。 我将数字划分为零件,并为每个零件使用了一个螺纹。
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);
}
}
结果必须是数字的总和,但是,由于列表中的线程异步分配,它是不正确的。请指出错误。
答案 0 :(得分:3)
这里有不止一件事,支撑你自己...
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# - 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
是的,这段代码负责多次调用Start
的情况。请注意,它每次都为输出创建一个新数组,并创建一个新的线程数组。这样,它就不会绊倒。
我让错误处理给你。提示:MaxNumber / ThreadsNumber
- &gt;除以0,(i + 1) * MaxNumber
- &gt;溢出,更不用说output.Sum()
- &gt;溢出。