wang2006zhi 发表于 2024-8-28 00:06:23

多线程-计算1到5万的乘积

本帖最后由 wang2006zhi 于 2025-2-14 12:16 编辑

int start = 1;
int end = 50000;
#region 线程实现
var sw1 = new Stopwatch();
sw1.Start();
int mid = (start + end) / 3;
// 使用三个任务分别计算前中后部分的乘积;
// 测试中当任务开到6个时候,用时550,效率提升不明显,说明任务也是有时间消耗,得综合考虑。
Task<BigInteger> task1 = Task.Run(() => Calculate(start, mid));
Task<BigInteger> task2 = Task.Run(() => Calculate(mid + 1, 2*mid));
Task<BigInteger> task3 = Task.Run(() => Calculate(2*mid + 1, end));
// 等待三个任务完成
Task.WaitAll(task1, task2, task3);

// 计算最终乘积
BigInteger result1 = task1.Result * task2.Result* task3.Result;
sw1.Stop();
#endregion

#region 常规实现
var sw2 = new Stopwatch();
sw2.Start();

//以下测试用时1200左右

var result2=Calculate(start, end);

//以下测试用时700左右
//var r1=Calculate(start, mid);
//var r2=Calculate(mid + 1, 2*mid);
//var r3=Calculate(2 * mid + 1, end);
//var result2 = r1 * r2 * r3;


sw2.Stop();
#endregion

Env.Editor.WriteMessage($"\n 计算结果相等?{result1==result2}" +
                        $"\n 线程/常规(毫秒):{sw1.ElapsedMilliseconds}/{sw2.ElapsedMilliseconds}");



static BigInteger Calculate(int start, int end)
{
    BigInteger product = 1;
    for (int i = start; i <= end; i++)
    {
      product *= i;
    }
    return product;
}

moy838840554 发表于 2024-8-28 10:49:23

建议开核心数/2 的任务数量,你这个3个任务可能你cpu是6核的.可以通过获取cpu核心数Environment.ProcessorCount/2动态创建任务数量

moy838840554 发表于 2024-8-28 10:53:25

本帖最后由 moy838840554 于 2024-8-28 10:55 编辑


当然,这个代码在你的电脑上跑效果可能没差别,得换到cpu核心数更多的电脑上跑
int start = 1;
      int end = 50000;
      int processorCount = Environment.ProcessorCount/2; // 获取 CPU 核心数/2
      int range =end / processorCount; // 计算每个线程的范围

      #region 线程实现
      var sw1 = new Stopwatch();
      sw1.Start();

      Task<BigInteger>[] tasks = new Task<BigInteger>;

      for (int i = 0; i < processorCount; i++)
      {
            int taskStart = start + i * range;
            int taskEnd = (i == processorCount - 1) ? end : taskStart + range - 1; // 确保最后一个任务处理到结束

            tasks = Task.Run(() => Calculate(taskStart, taskEnd));
      }

      // 等待所有任务完成
      Task.WaitAll(tasks);

      // 计算最终乘积
      BigInteger result1 = 1;
      foreach (var task in tasks)
      {
            result1 *= task.Result;
      }

      sw1.Stop();
    #endregion

    #region 常规实现
    var sw2 = new Stopwatch();
    sw2.Start();

    var result2 = Calculate(start, end);

    sw2.Stop();
#endregion

Env.Editor.WriteMessage($"\n 计算结果相等?{result1 == result2}" +
      $"\n 线程/常规(毫秒):{sw1.ElapsedMilliseconds}/{sw2.ElapsedMilliseconds}");

wang2006zhi 发表于 2024-8-28 12:03:26

本帖最后由 wang2006zhi 于 2024-8-28 12:05 编辑

moy838840554 发表于 2024-8-28 10:53
当然,这个代码在你的电脑上跑效果可能没差别,得换到cpu核心数更多的电脑上跑经过实测。。可能数据量还不够大,体现不了线程优势。。。另外实测,常规计算中也可以拆分10分部计算在合并计算,时间差不多
命令: TT1
计算结果相等?True,核心数20
线程/常规(毫秒):552/1118
命令: TT1
计算结果相等?True,核心数20
线程/常规(毫秒):564/1110
命令: TT1
计算结果相等?True,核心数20
线程/常规(毫秒):724/1123
命令: TT1
计算结果相等?True,核心数20

你有种再说一遍 发表于 2024-8-28 15:24:39

本帖最后由 你有种再说一遍 于 2024-8-28 19:30 编辑

wang2006zhi 发表于 2024-8-28 12:03
经过实测。。可能数据量还不够大,体现不了线程优势。。。另外实测,常规计算中也可以拆分10分部计算在合并 ...

1,你没有用Parallel.For
2,你没学循环展开,不知道CPU分支流水线技术.
3,一定要具体任务具体学习.你采用bigint了,它不是基础的数值类型,它还是不定长内存,除非你做银行业务,不然用这个干什么...光学新建几个线程本身是不行的,还要结合使用SIMD等等.
页: [1]
查看完整版本: 多线程-计算1到5万的乘积