C#开发基础之多线程技术应用之并行下载并通知运行状态

1. 场景描述:

在面对需要下载大量文件或同时执行大量任务的挑战时,多线程技术成为提高效率的关键工具。这种方法就像优化了的生产线,各个部分同步协作,使整体工作更为流畅和迅速。

想象一下,你面临一个包含1,000个文件下载或者有多个可以并行处理的任务的时候。在传统的单线程模型中,这将是一个漫长的等待过程。但通过多线程,我们可以将这个大任务拆分成多个小任务并行执行,从而大大缩短完成时间,提高整体效率。

然而,仅仅完成任务是不够的。为了确保用户和其他相关方实时了解进展情况,我们需要一个高效的进度跟踪和报告机制。这可以通过建立实时监控系统或利用进度条、日志和通知来实现。这样用户,都可以在任何时候了解项目的当前状态和预期进度。

下面就思考两种实现方式:多线程和并行库的用法。主要是体会使用模型的使用。

2. 多线程处理实现过程:

示例代码:

using System;
using System.Threading;
using System.Threading.Tasks;

class Program
{
    static int currentFile = 0;
    static object lockObject = new object();

    static void Main(string[] args)
    {
        const int NUM_FILES = 1000;
        const int NUM_THREADS = 20;

        // 创建一个计数器,以便在所有线程完成后更新状态。
        CountdownEvent countdown = new CountdownEvent(NUM_THREADS);

        // 创建20个下载线程
        for (int i = 0; i < NUM_THREADS; i++)
        {
            int threadIndex = i;
            Task.Run(() =>
            {
                // 模拟下载过程
                for (int j = threadIndex; j < NUM_FILES; j += NUM_THREADS)
                {
                    DownloadFile(j);
                    ReportProgress(NUM_FILES);  // 报告下载进度
                }

                // 当前线程完成下载,向计数器发信号。
                countdown.Signal();
            });
        }

        // 等待所有线程完成
        countdown.Wait();

        // 所有线程都已完成,将状态标记为已完成。
        Console.WriteLine("所有文件下载已完成!");
    }

    static void DownloadFile(int fileIndex)
    {
        // 模拟下载过程
        Thread.Sleep(100);   // 假设每个文件需要10毫秒下载
        Console.WriteLine($"文件 #{fileIndex} 已下载完毕!");
    }

    static void ReportProgress(int NUM_FILES)
    {
        lock(lockObject)
        {
            int current = Interlocked.Increment(ref currentFile);
            Console.WriteLine($"当前进度:{current}/{NUM_FILES}");
        }
    }
}

在这里插入图片描述

  1. 定义常量:NUM_FILES表示总文件数量,这里设定为1000。NUM_THREADS表示线程数量,这里设定为20。
  2. 创建一个CountdownEvent对象countdown,用于在所有线程完成后更新状态。
  3. 创建20个下载线程:使用Task.Run()创建一个任务,并在每个任务中执行相应的操作,每个线程根据索引threadIndex来决定从哪个文件开始下载,通过循环递增NUM_THREADS来实现线程间的文件分配。 在循环中调用DownloadFile()方法来模拟下载文件,并在下载完成后调用ReportProgress()方法报告下载进度。 当线程完成下载后,通过countdown.Signal()向计数器发信号。
  4. 使用countdown.Wait()等待所有线程完成。
  5. 所有线程都已完成,输出提示信息:“所有文件下载已完成!”
  6. DownloadFile()方法模拟文件下载过程:使用Thread.Sleep()方法来模拟下载耗时,这里假设每个文件需要100毫秒下载。输出下载完毕的文件信息。
  7. ReportProgress()方法用于报告下载进度:使用lock语句加锁,确保多个线程同时操作currentFile变量时不会产生竞争条件。使用Interlocked.Increment()原子地递增currentFile变量,获取当前下载的文件数。输出当前下载进度信息。

小结

总体而言,该程序通过创建多个线程来并行下载文件,并使用计数器来跟踪所有线程的完成状态。每个线程负责下载部分文件,并在完成后报告当前的下载进度。最后,当所有线程都完成时,输出下载完成的提示信息。

3. 使用并行库的处理过程:

示例代码:

using System;
using System.Threading.Tasks;
using System.Collections.Concurrent;
using System.Diagnostics;

namespace ParallelApp
{
    class Program
    {
        static int currentFile = 0;
        static object lockObject = new object();
        static void Main(string[] args)
        {
            const int NUM_FILES = 1000;

            // 使用ParallelOptions来跟踪并行任务的进度
            var parallelOptions = new ParallelOptions
            {
                MaxDegreeOfParallelism = 20 // 设置最大并行度
            };
            Stopwatch stopWatch = new Stopwatch();
            stopWatch.Start();
            // 使用Parallel.ForEach并行下载文件
            Parallel.ForEach(
                Partitioner.Create(0, NUM_FILES), // 使用分区器创建任务范围
                parallelOptions,
                (range, loopState) =>
                {
                    for (int j = range.Item1; j < range.Item2; j++)
                    {
                        DownloadFile(j);
                        ReportProgress(NUM_FILES);  // 报告下载进度
                    }
                });
            stopWatch.Stop();
            // 所有任务完成后输出信息
            Console.WriteLine("所有文件下载已完成!" + stopWatch.ElapsedMilliseconds + "ms");
        }
        static void DownloadFile(int fileIndex)
        {
            // 模拟下载过程
            Task.Delay(100).Wait(); // 假设每个文件需要100毫秒下载
            Console.WriteLine($"文件 #{fileIndex} 已下载完毕!");
        }
        static void ReportProgress(int NUM_FILES)
        {
            lock (lockObject)
            {
                int current = Interlocked.Increment(ref currentFile);
                Console.WriteLine($"当前进度:{current}/{NUM_FILES}");
            }
        }
    }
}

在这里插入图片描述

  1. 初始化并行任务:使用并行库,你可以利用Parallel.For或Parallel.ForEach来简化并行任务的创建和管理。这些方法会自动处理任务的分发和管理,无需手动管理线程。
  2. 分发任务:Parallel.For或Parallel.ForEach会根据可用的处理器核心数或任务的工作量自动分发任务。你只需指定任务的起始和结束范围。
  3. 报告进度:由于Parallel类提供了内置的机制来跟踪任务的进度,所以你可能需要使用ParallelOptions和ParallelLoopState来跟踪和报告任务的进度。

小结:

在这个版本中,我们使用Parallel.ForEach来并行处理文件的下载任务。我们使用了Partitioner.Create来创建任务的范围,并使用ParallelOptions来控制并行度。此外,进度报告也被稍微简化,因为Parallel类本身提供了一个更简洁的方法来处理这种情况。

4. 参考文档:

  • 33
    点赞
  • 29
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

dotnet研习社

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值