您所询问的内容是完全可行的,具体如下。
int counter = 0;
var tasks = new List<Task>();
var arr = Enumerable.Range(0, 921600).ToArray();
tasks.Add(Task.Factory.StartNew(() =>
{
for (int i = 0; i < 921600 / 2; i++)
{
if (arr[i] > 240) counter++;
}
}));
tasks.Add(Task.Factory.StartNew(() =>
{
for (int j = 921600 / 2; j < 921600; j++)
{
if (arr[j] > 240) counter++;
}
}));
Task.WaitAll(tasks.ToArray());
不要使用此代码!您将遇到竞争条件,其中一个线程的增量由于“读取,读取,写入,写入”情况而丢失。在LinqPad中运行此代码,我最终得到的计数器值在600,000和800,000之间。显然,这个范围远不及实际值。
解决此竞争条件的方法是引入锁定,这意味着只有一个线程可以同时触摸变量。这消除了分配为多线程的能力,但允许我们获得正确的答案。(参考我的机器需要0.042秒)
int counter = 0;
var tasks = new List<Task>();
var arr = Enumerable.Range(0, 921600).ToArray();
var locker = new Object();
tasks.Add(Task.Factory.StartNew(() =>
{
for (int i = 0; i < 921600 / 2; i++)
{
if (arr[i] > 240)
lock (locker)
counter++;
}
}));
tasks.Add(Task.Factory.StartNew(() =>
{
for (int j = 921600 / 2; j < 921600; j++)
{
if (arr[j] > 240)
lock (locker)
counter++;
}
}));
Task.WaitAll(tasks.ToArray());
解决方案确实是使用Dmitry建议的并行Linq:
Enumerable.Range(0, 921600).AsParallel().Count(x=>x>240);
这需要0.031秒,比我们的锁定代码更快,仍然返回正确的答案,但是去掉 AsParallel 调用后,它可以在 0.024 秒内运行。并行运行一段代码会引入管理线程的开销。有时性能提升超过了这个开销,但是很多时候却没有。
故事的寓意是,始终针对你预期的数据运行一些度量/时间测量,并检查是否实际上存在性能优势。
Parallel.For
吗?它不一定会分成两部分,但我认为根据系统的不同,你会得到非常好的提升。 - Philippe Parécounter
,请确保使用类似 Interlocked.Increment() 的方法正确地执行它。 - itsme86