You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

Async/Await网络请求示例疑问:如何实现异步下载与计算并行?

async/await 实际应用与异步任务调度问题

我完全搞不懂async/await的实际应用意义。作为初学者,我知道async/await多用于线程需要长时间等待的操作(比如数据库响应、网络请求、文件处理),而非长耗时计算;也看到资料说它用来避免阻塞主线程,但我疑惑为什么不用创建Task的方式来避免阻塞。

我写了一段包含网络下载和CPU密集型循环的代码,结果发现while(i < int.MaxValue)操作会先占满CPU资源,等它执行完后下载才开始,整个示例耗时约54秒。请问怎么让下载异步执行的同时,线程能处理while循环任务,避免资源闲置?

using System.Net;

string PathProject = Directory.GetParent(Directory.GetCurrentDirectory()).Parent.Parent.Parent.FullName;

//Create folder 1 in the project folder
DirectoryInfo Path = new DirectoryInfo($"{PathProject}\\1");

int Iterations = Environment.ProcessorCount * 3;
string file = "https://s182vla.storage.yandex.net/rdisk/82b08d86b9920a5e889c6947e4221eb1350374db8d799ee9161395f7195b0b0e/62f75403/geIEA69cusBRNOpxmtup5BdJ7AbRoezTJE9GH4TIzcUe-Cp7uoav-lLks4AknK2SfU_yxi16QmxiuZOGFm-hLQ==?uid=0&filename=004%20-%2002%20Lesnik.mp3&disposition=attachment&hash=e0E3gNC19eqNvFi1rXJjnP1y8SAS38sn5%2ByGEWhnzE5cwAGsEnlbazlMDWSjXpyvq/J6bpmRyOJonT3VoXnDag%3D%3D&limit=0&content_type=audio%2Fmpeg&owner_uid=160716081&fsize=3862987&hid=98984d857027117759bc5ce6092eaa6a&media_type=audio&tknv=v2&rtoken=k9xogU6296eg&force_default=no&ycrid=na-2bc914314062204f1cbf810798018afd-downloader16e&ts=5e61a6daac6c0&s=eef8b08190dc7b22befd6bad89e1393b394869a1668d9b8af3730cce4774e8ad&pb=U2FsdGVkX1__q3AvjJzgzWG4wVR80Oh8XMl-0Dlfyu9FhqAYQVVkoBV0dtBmajpmOkCXKUXPbREOS-MZCxMNu2rkAkKq_n-AXcZ85svtSFs";

List<Task> tasks = new List<Task>();

void MyMethod1(int i)
{
    WebClient client = new WebClient();
    client.DownloadFile(file, $"{Path}\\{i}.mp3");
}

void MyMethod2()
{
    int i = 0;
    while (i < int.MaxValue)
    {
        i++;
    }
}

DateTime dateTimeStart = DateTime.Now;

for (int i = 0; i < Iterations; i++)
{
    int j = i;
    tasks.Add(Task.Run(() => MyMethod1(j)));
}

for (int i = 0; i < Iterations; i++)
{
    tasks.Add(Task.Run(() => { MyMethod2(); MyMethod2(); }));
}

Task.WaitAll(tasks.ToArray());

Console.WriteLine(DateTime.Now - dateTimeStart);

while (true)
{
    Thread.Sleep(100);

    if (Path.GetFiles().Length == Iterations)
    {
        Thread.Sleep(1000);

        foreach (FileInfo f in Path.GetFiles())
        {
            f.Delete();
        }

        return;
    }
}

问题原因

你的代码出现下载等待CPU任务完成的核心原因:

  • WebClient.DownloadFile是同步IO方法,即便用Task.Run包裹,它也会持续占用线程池线程直到下载完成。
  • 你创建了Environment.ProcessorCount * 3个CPU密集任务,这些任务会占满线程池的工作线程(线程池默认根据CPU核心数调整并发数),没有空闲线程处理下载任务,导致下载必须等CPU任务释放线程后才能启动。

解决方法

要让下载和CPU任务并行执行,核心是使用真正的异步IO方法配合async/await,这样IO等待时不会占用线程池线程,线程可被调度去处理CPU密集任务。

修改后的代码示例

using System.Net.Http;

string PathProject = Directory.GetParent(Directory.GetCurrentDirectory()).Parent.Parent.Parent.FullName;
DirectoryInfo Path = new DirectoryInfo($"{PathProject}\\1");
int Iterations = Environment.ProcessorCount * 3;
string file = "https://s182vla.storage.yandex.net/rdisk/82b08d86b9920a5e889c6947e4221eb1350374db8d799ee9161395f7195b0b0e/62f75403/geIEA69cusBRNOpxmtup5BdJ7AbRoezTJE9GH4TIzcUe-Cp7uoav-lLks4AknK2SfU_yxi16QmxiuZOGFm-hLQ==?uid=0&filename=004%20-%2002%20Lesnik.mp3&disposition=attachment&hash=e0E3gNC19eqNvFi1rXJjnP1y8SAS38sn5%2ByGEWhnzE5cwAGsEnlbazlMDWSjXpyvq/J6bpmRyOJonT3VoXnDag%3D%3D&limit=0&content_type=audio%2Fmpeg&owner_uid=160716081&fsize=3862987&hid=98984d857027117759bc5ce6092eaa6a&media_type=audio&tknv=v2&rtoken=k9xogU6296eg&force_default=no&ycrid=na-2bc914314062204f1cbf810798018afd-downloader16e&ts=5e61a6daac6c0&s=eef8b08190dc7b22befd6bad89e1393b394869a1668d9b8af3730cce4774e8ad&pb=U2FsdGVkX1__q3AvjJzgzWG4wVR80Oh8XMl-0Dlfyu9FhqAYQVVkoBV0dtBmajpmOkCXKUXPbREOS-MZCxMNu2rkAkKq_n-AXcZ85svtSFs";

List<Task> tasks = new List<Task>();

// 异步下载方法
async Task MyAsyncDownload(int i)
{
    using var httpClient = new HttpClient();
    var fileBytes = await httpClient.GetByteArrayAsync(file);
    await File.WriteAllBytesAsync($"{Path}\\{i}.mp3", fileBytes);
}

void MyCpuIntensiveTask()
{
    int i = 0;
    while (i < int.MaxValue)
    {
        i++;
    }
}

DateTime dateTimeStart = DateTime.Now;

// 添加异步下载任务
for (int i = 0; i < Iterations; i++)
{
    int j = i;
    tasks.Add(MyAsyncDownload(j));
}

// 添加CPU密集任务
for (int i = 0; i < Iterations; i++)
{
    tasks.Add(Task.Run(() => { MyCpuIntensiveTask(); MyCpuIntensiveTask(); }));
}

await Task.WhenAll(tasks);

Console.WriteLine(DateTime.Now - dateTimeStart);

// 后续清理逻辑
while (true)
{
    await Task.Delay(100);
    if (Path.GetFiles().Length == Iterations)
    {
        await Task.Delay(1000);
        foreach (FileInfo f in Path.GetFiles())
        {
            f.Delete();
        }
        return;
    }
}

async/await 的实际意义

  • 线程高效利用:异步IO操作(如网络下载)等待响应时,await会释放当前线程,让线程池将该线程调度去处理其他任务(比如你的CPU密集循环),避免线程在等待时闲置。
  • 代码可读性:async/await让异步代码的结构和同步代码几乎一致,比回调、手动管理Task的方式更易维护。
  • 避免阻塞主线程:在UI应用中,主线程阻塞会导致界面卡死,async/await能让主线程在等待IO时继续处理用户交互——这是Task.Run做不到的(Task.Run只是把任务扔到后台线程,主线程若调用Task.WaitAll仍会被阻塞)。

注意:Task.Run适合处理CPU密集型任务,可将计算任务放到后台线程避免阻塞主线程;但IO密集型任务用async/await配合异步API才是最优解,因为不需要占用线程等待IO响应。

内容的提问来源于stack exchange,提问作者user19747524

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.08.23 05:45:29