多線程技術(shù)應(yīng)用之并行下載并通知運(yùn)行狀態(tài)

場(chǎng)景描述:
在面對(duì)需要下載大量文件或同時(shí)執(zhí)行大量任務(wù)的挑戰(zhàn)時(shí),多線程技術(shù)成為提高效率的關(guān)鍵工具。這種方法就像優(yōu)化了的生產(chǎn)線,各個(gè)部分同步協(xié)作,使整體工作更為流暢和迅速。
想象一下,你面臨一個(gè)包含1,000個(gè)文件下載或者有多個(gè)可以并行處理的任務(wù)的時(shí)候。在傳統(tǒng)的單線程模型中,這將是一個(gè)漫長(zhǎng)的等待過(guò)程。但通過(guò)多線程,我們可以將這個(gè)大任務(wù)拆分成多個(gè)小任務(wù)并行執(zhí)行,從而大大縮短完成時(shí)間,提高整體效率。
然而,僅僅完成任務(wù)是不夠的。為了確保用戶和其他相關(guān)方實(shí)時(shí)了解進(jìn)展情況,我們需要一個(gè)高效的進(jìn)度跟蹤和報(bào)告機(jī)制。這可以通過(guò)建立實(shí)時(shí)監(jiān)控系統(tǒng)或利用進(jìn)度條、日志和通知來(lái)實(shí)現(xiàn)。這樣用戶,都可以在任何時(shí)候了解項(xiàng)目的當(dāng)前狀態(tài)和預(yù)期進(jìn)度。
下面就思考兩種實(shí)現(xiàn)方式:多線程和并行庫(kù)的用法。主要是體會(huì)使用模型的使用。
多線程處理實(shí)現(xiàn)過(guò)程:
示例代碼:
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;
// 創(chuàng)建一個(gè)計(jì)數(shù)器,以便在所有線程完成后更新狀態(tài)。
CountdownEvent countdown = new CountdownEvent(NUM_THREADS);
// 創(chuàng)建20個(gè)下載線程
for (int i = 0; i < NUM_THREADS; i++)
{
int threadIndex = i;
Task.Run(() =>
{
// 模擬下載過(guò)程
for (int j = threadIndex; j < NUM_FILES; j += NUM_THREADS)
{
DownloadFile(j);
ReportProgress(NUM_FILES); // 報(bào)告下載進(jìn)度
}
// 當(dāng)前線程完成下載,向計(jì)數(shù)器發(fā)信號(hào)。
countdown.Signal();
});
}
// 等待所有線程完成
countdown.Wait();
// 所有線程都已完成,將狀態(tài)標(biāo)記為已完成。
Console.WriteLine("所有文件下載已完成!");
}
static void DownloadFile(int fileIndex)
{
// 模擬下載過(guò)程
Thread.Sleep(100); // 假設(shè)每個(gè)文件需要10毫秒下載
Console.WriteLine($"文件 #{fileIndex} 已下載完畢!");
}
static void ReportProgress(int NUM_FILES)
{
lock(lockObject)
{
int current = Interlocked.Increment(ref currentFile);
Console.WriteLine($"當(dāng)前進(jìn)度:{current}/{NUM_FILES}");
}
}
}
定義常量:NUM_FILES表示總文件數(shù)量,這里設(shè)定為1000。NUM_THREADS表示線程數(shù)量,這里設(shè)定為20。
創(chuàng)建一個(gè)CountdownEvent對(duì)象countdown,用于在所有線程完成后更新狀態(tài)。
創(chuàng)建20個(gè)下載線程:
- 使用Task.Run()創(chuàng)建一個(gè)任務(wù),并在每個(gè)任務(wù)中執(zhí)行相應(yīng)的操作。
- 每個(gè)線程根據(jù)索引threadIndex來(lái)決定從哪個(gè)文件開始下載,通過(guò)循環(huán)遞增NUM_THREADS來(lái)實(shí)現(xiàn)線程間的文件分配。
- 在循環(huán)中調(diào)用DownloadFile()方法來(lái)模擬下載文件,并在下載完成后調(diào)用ReportProgress()方法報(bào)告下載進(jìn)度。
- 當(dāng)線程完成下載后,通過(guò)countdown.Signal()向計(jì)數(shù)器發(fā)信號(hào)。
使用countdown.Wait()等待所有線程完成。
所有線程都已完成,輸出提示信息:“所有文件下載已完成!”
DownloadFile()方法模擬文件下載過(guò)程:使用Thread.Sleep()方法來(lái)模擬下載耗時(shí),這里假設(shè)每個(gè)文件需要100毫秒下載。輸出下載完畢的文件信息。
ReportProgress()方法用于報(bào)告下載進(jìn)度:
- 使用lock語(yǔ)句加鎖,確保多個(gè)線程同時(shí)操作currentFile變量時(shí)不會(huì)產(chǎn)生競(jìng)爭(zhēng)條件。
- 使用Interlocked.Increment()原子地遞增currentFile變量,獲取當(dāng)前下載的文件數(shù)。
- 輸出當(dāng)前下載進(jìn)度信息。
總體而言,該程序通過(guò)創(chuàng)建多個(gè)線程來(lái)并行下載文件,并使用計(jì)數(shù)器來(lái)跟蹤所有線程的完成狀態(tài)。每個(gè)線程負(fù)責(zé)下載部分文件,并在完成后報(bào)告當(dāng)前的下載進(jìn)度。最后,當(dāng)所有線程都完成時(shí),輸出下載完成的提示信息。
使用并行庫(kù)的處理過(guò)程:
示例代碼:
using System;
using System.Threading.Tasks;
using System.Collections.Concurrent;
class Program
{
static int currentFile = 0;
static object lockObject = new object();
static void Main(string[] args)
{
const int NUM_FILES = 1000;
// 使用ParallelOptions來(lái)跟蹤并行任務(wù)的進(jìn)度
var parallelOptions = new ParallelOptions
{
MaxDegreeOfParallelism = 20 // 設(shè)置最大并行度
};
// 使用Parallel.ForEach并行下載文件
Parallel.ForEach(
Partitioner.Create(0, NUM_FILES), // 使用分區(qū)器創(chuàng)建任務(wù)范圍
parallelOptions,
(range, loopState) =>
{
for (int j = range.Item1; j < range.Item2; j++)
{
DownloadFile(j);
ReportProgress(NUM_FILES); // 報(bào)告下載進(jìn)度
}
});
// 所有任務(wù)完成后輸出信息
Console.WriteLine("所有文件下載已完成!");
}
static void DownloadFile(int fileIndex)
{
// 模擬下載過(guò)程
Task.Delay(100).Wait(); // 假設(shè)每個(gè)文件需要100毫秒下載
Console.WriteLine($"文件 #{fileIndex} 已下載完畢!");
}
static void ReportProgress(int NUM_FILES)
{
lock(lockObject)
{
int current = Interlocked.Increment(ref currentFile);
Console.WriteLine($"當(dāng)前進(jìn)度:{current}/{NUM_FILES}");
}
}
}
初始化并行任務(wù):使用并行庫(kù),你可以利用Parallel.For或Parallel.ForEach來(lái)簡(jiǎn)化并行任務(wù)的創(chuàng)建和管理。這些方法會(huì)自動(dòng)處理任務(wù)的分發(fā)和管理,無(wú)需手動(dòng)管理線程。
分發(fā)任務(wù):Parallel.For或Parallel.ForEach會(huì)根據(jù)可用的處理器核心數(shù)或任務(wù)的工作量自動(dòng)分發(fā)任務(wù)。你只需指定任務(wù)的起始和結(jié)束范圍。
報(bào)告進(jìn)度:由于Parallel類提供了內(nèi)置的機(jī)制來(lái)跟蹤任務(wù)的進(jìn)度,所以你可能需要使用ParallelOptions和ParallelLoopState來(lái)跟蹤和報(bào)告任務(wù)的進(jìn)度。
在這個(gè)版本中,我們使用Parallel.ForEach來(lái)并行處理文件的下載任務(wù)。我們使用了Partitioner.Create來(lái)創(chuàng)建任務(wù)的范圍,并使用ParallelOptions來(lái)控制并行度。此外,進(jìn)度報(bào)告也被稍微簡(jiǎn)化,因?yàn)镻arallel類本身提供了一個(gè)更簡(jiǎn)潔的方法來(lái)處理這種情況。






















