1const n=`--- 2title: C#å¤çº¿ç¨ä¸è½½æä»¶ 3description: C#å¤çº¿ç¨å¹¶åä¸è½½æä»¶ 4author: ShenNya 5date: 2024-12-08 02:06:00 +0800 6categories: [CSharp] 7tags: [CSharp, å¤çº¿ç¨, Thread, å¹¶å, ä¸è½½] 8#pin: true 9math: true 10mermaid: true 11--- 12 13# C#å¤çº¿ç¨ä¸è½½æä»¶ 14 15## åè¨ 16 17> è¿æ®µå°±ç®æ¯ç®åå¤çº¿ç¨çå®æäº 18> 19> 妿éè¦å¤çº¿ç¨å ¥é¨ï¼ç¹[è¿é](/posts/csharp-multi-thread/) 20 21æ¥å¸¸ä¸ç»å¸¸ä¼éå°åç§ä¸è½½æ ¢çæ åµï¼å¾å¤è½¯ä»¶å¯ä»¥è§£å³è¿ä¸ªé®é¢ï¼å¦IDMãFDMãaria2cï¼çè³æ¯æMCå¯å¨å¨PCL 22 23æ¬è´¨ä¸ä»ä»¬é½æ¯å¤çº¿ç¨ä¸è½½ï¼å ¶ä¸»è¦ææ³æ¯å°æä»¶**åç** 24 25å设æä»¬æå¤§å°ä¸º255byteçæä»¶ï¼ä¸ä¸ªäººä¸å°±æ¯ä»0ä¸è½½ç´å°255ç»æ 26 27è¿ä¸ªæ¶åï¼å¦æå°æä»¶å为å¤çï¼å»ºç«å¤ä¸ªçº¿ç¨åæ¶ä¸è½½ï¼å°±ç¸å½äºæå¤ä¸ªäººå¨å¸®ä½ ä¸è½½åä¸ä¸ªæä»¶äºï¼ä¹å°±å®ç°äºå éï¼BTç§å乿¯è¿æ ·ï¼ä¹è®¸ä»¥åæ°å¦å¥½äºæä¹ä¼åä¸ç¯è¿ä¸ªçç¬è®°ï¼ 28 29 30 31## 代ç åç¨ä¾ 32è¿éå 贴代ç ï¼ 33 34ä»ä»¥ä¸URLä¸è½½æä»¶ï¼ 35\`\`\` 36http://192.168.10.199/p/Local%20500G%20%E5%91%9C%E5%91%9C/SurfacePro3_Win10_18362_1902002_0.msi?sign=hwUl4yfX8aRrmAb-U0oqcyELDWUY_XxPzlFHXQuVjUk=:0 37\`\`\` 38使ç¨äºæµè§å¨çUAï¼ 39\`\`\` 40Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/130.0.0.0 Safari/537.36 Edg/130.0.0.0 41\`\`\` 42ä¿åå°\`a.msi\` 43 44\`\`\`csharp 45using System; 46using System.IO; 47using System.Net.Http; 48using System.Threading.Tasks; 49using System.Threading; 50 51public class Range 52{ 53 public long from { get; set; } 54 public long to { get; set; } 55} 56 57public class Downloader 58{ 59 private readonly object _lock = new object(); 60 private FileStream FileStream { get; set; } 61 private int TotalThread { get; set; } 62 private int CurrentThread { get; set; } 63 private string Url { get; set; } 64 private string UserAgent { get; set; } 65 66 private long downloadSize = 0; 67 private int downloadProgress = 0; 68 private long downloaded = 0; 69 private long totalSize = 0; 70 public Downloader(string url, string user_agent, string file_path, int thread) 71 { 72 TotalThread = thread; 73 CurrentThread = 0; 74 Url = url; 75 UserAgent = user_agent; 76 FileStream = new FileStream(file_path, FileMode.OpenOrCreate, FileAccess.ReadWrite); 77 } 78 public async Task<long> GetInfo() 79 { 80 using (HttpClient client = new HttpClient()){ 81 client.DefaultRequestHeaders.Add("User-Agent", UserAgent); 82 using (HttpResponseMessage response = await client.GetAsync(Url)) 83 { 84 response.EnsureSuccessStatusCode(); 85 86 totalSize = response.Content.Headers.ContentLength ?? 0; 87 FileStream.SetLength(totalSize); 88 Console.WriteLine(totalSize.ToString()); 89 return totalSize; 90 } 91 } 92 return 0; 93 } 94 public List<Range> CalcRange() 95 { 96 var range = new List<Range>(); 97 long start = 0; 98 if (totalSize > 0) 99 { 100 long average_size = totalSize / TotalThread; 101 for (int i = 0; i < TotalThread; i++) 102 { 103 if (i == TotalThread - 1) 104 { 105 range.Add(new Range 106 { 107 from = start, 108 to = totalSize 109 }); 110 } 111 else 112 { 113 range.Add(new Range 114 { 115 from = start, 116 to = start + average_size 117 }); 118 start += average_size; 119 } 120 } 121 } 122 return range; 123 } 124 public async Task DownloadThread(Range range) 125 { 126 127 long pointer = range.from; 128 using (HttpClient client = new HttpClient()) 129 { 130 client.DefaultRequestHeaders.Add("Range", $"bytes={range.from}-{range.to}"); 131 client.DefaultRequestHeaders.Add("User-Agent", UserAgent); 132 using (HttpResponseMessage response = await client.GetAsync(Url)) 133 { 134 response.EnsureSuccessStatusCode(); 135 136 long? totalSize = response.Content.Headers.ContentLength; 137 using (Stream contentStream = await response.Content.ReadAsStreamAsync()) 138 { 139 byte[] buffer = new byte[8192]; 140 int bytesRead; 141 while ((bytesRead = await contentStream.ReadAsync(buffer, 0, buffer.Length)) > 0) 142 { 143 //await write 144 downloaded += bytesRead; 145 lock (_lock) 146 { 147 Console.WriteLine("WriteTo: " + pointer.ToString() + " By: " + range.from.ToString()); 148 149 FileStream.Seek(pointer, SeekOrigin.Begin); 150 FileStream.Write(buffer, 0, bytesRead); 151 } 152 pointer += bytesRead; 153 154 } 155 } 156 } 157 } 158 } 159 public void releaseFile() 160 { 161 Console.WriteLine("File has been relese"); 162 lock (_lock) 163 { 164 FileStream.Close(); 165 } 166 } 167 public async void Run() 168 { 169 long size = await GetInfo(); 170 Console.WriteLine("Getinfo complete, size: " + totalSize.ToString()); 171 //CountdownEvent countDown = new CountdownEvent(TotalThread); 172 List<Task> tasks = new List<Task>(); 173 foreach (Range range in CalcRange()) 174 { 175 Console.WriteLine("Thread start: " + range.from.ToString()); 176 tasks.Add(DownloadThread(range)); 177 } 178 await Task.WhenAll(tasks); 179 //countDown.Wait(); 180 releaseFile(); 181 } 182} 183 184class Program 185{ 186 static void Main(string[] args) 187 { 188 Downloader download = new Downloader("http://192.168.10.199/p/Local%20500G
188%20%E5%91%9C%E5%91%9C/SurfacePro3_Win10_18362_1902002_0.msi?sign=hwUl4yfX8aRrmAb-U0oqcyELDWUY_XxPzlFHXQuVjUk=:0", "Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/130.0.0.0 Safari/537.36 Edg/130.0.0.0", "a.msi", 32); 189 download.Run(); 190 Console.ReadLine(); 191 } 192} 193\`\`\` 194## æç¡®éæ± 195 196åå说å°å¤çº¿ç¨ä¸è½½æä»¶çæ ¸å¿æ¯åçï¼é£ä¹å°±å¼ç³åºäºå 个é®é¢ï¼ 197 198- å¦ä½æåé¢ç¥æä»¶çå¤§å° 199- å¦ä½è®¡ç®æ¯ä¸ªåçç大å°å¹¶è®©æå¡å¨è¿å对åºåç 200- å¦ä½å°è¿äºåçæ´å 201 202 203## é¢ç¥å¤§å° 204 205å¨HTTPä¼ è¾ä¸ï¼æ£æä¼å å«ä¸ä¸ªæ¥æè®¸å¤ä¿¡æ¯ç\`头ï¼Headerï¼\`ï¼å¤´éé¢ä¸è¬ä¼æä¸ä¸ªå«\`Content-Length\`çå¼ï¼è¿å°±æ¯bodyç大å°ï¼ä¹å°±æ¯æä»¬æä»¶çå¤§å° 206 207æå¼ä½ çæµè§å¨ï¼æä¸\`F12\`æå¼å¼åè å·¥å ·ï¼éæ©ç½ç»ï¼å·æ°ä¸ä¸ç½é¡µï¼å°±å¯ä»¥çå°\`Response Headers\`ä¸å 嫿\`Content-Length\` 208 209 210 211è¿ä¸ªæ°å¼å°±æ¯è¿ä¸ªé¡µé¢çå¤§å° 212 213å¯ä»¥ä½¿ç¨ä»¥ä¸ä»£ç è·åï¼ 214\`\`\`csharp 215long totalSize; 216using (HttpClient client = new HttpClient()){ 217 using (HttpResponseMessage response = await client.GetAsync(Url)) 218 { 219 response.EnsureSuccessStatusCode(); 220 totalSize = response.Content.Headers.ContentLength ?? 0; 221 FileStream.SetLength(totalSize); 222 Console.WriteLine("æä»¶å¤§å°ä¸º: " + totalSize.ToString()); 223 return totalSize; 224 } 225} 226return 0; 227\`\`\` 228> ç±äºæä»¶ä¸è¬å¾å¤§ï¼æä»¶å¤§å°æå¥½ç¨\`long\`åå¨ 229> 230> å¦ææ æ³è·åï¼æå¯è½æ¯è¿ä¸ªé¡µé¢ä¸æ¯æ\`Content-Length\`ææ¯æªç»ææï¼å¯ä»¥å°è¯\`client.DefaultRequestHeaders.Add\`è¯å¥æ·»å \`User-Agent\`å\`Host\` 231{: .prompt-info } 232 233## 计ç®åçå¤§å° 234 235è¿éæä»¬ç®å以ä¸ï¼å设æ255é¿åº¦çæä»¶ï¼ä¸ºäºæ¹ä¾¿è¡¨ç¤ºæä»¬æä»åæä¸çç两çï¼ 236 237 238 239å¯ä»¥çå°ç¬¬ä¸æ®µçèå´ä¸º\`0-63\` 240 241ç¬¬äºæ®µèå´ä¸º\`64-255\` 242 243å°è¿ä¸¤æ®µåå¹¶å³ä¸ºæ´ä¸ªæä»¶ 244 245è¿éçè§åæ¯ï¼ç¬¬ä¸æ®µä»¥\`0\`为起ç¹ï¼\`n\`为ç»ç¹ï¼å䏿®µä»¥\`n+1\`为起ç¹\`m\`为ç»ç¹ï¼ä»¥æ¤ç±»æ¨ï¼ç´å°ç»ç¹ä¸ºæä»¶æ»é¿åº¦ 246 247--- 248 249æ¢ç¶è¦ä½¿ç¨å¤çº¿ç¨ï¼é£ä¹å°±è¦å®ä¹çº¿ç¨æ°ï¼æ¯ä¸ªçº¿ç¨ä¸è½½å¯¹åºçé¨åï¼è¿é使ç¨ååçæ¹æ³ï¼ 250 251 252\`\`\`csharp 253 254public class Range 255{ 256 public long from { get; set; } 257 public long to { get; set; } 258} 259 260private long totalSize = 0; // å¨è¿éå®ä¹æä»¶å¤§å° 261private int TotalThread { get; set; }// å¨è¿éå®ä¹çº¿ç¨æ° 262 263public List<Range> CalcRange() 264{ 265 var range = new List<Range>(); 266 long start = 0; 267 if (totalSize > 0) 268 { 269 long average_size = totalSize / TotalThread; 270 for (int i = 0; i < TotalThread; i++) 271 { 272 if (i == TotalThread - 1) // æå䏿®µè¦æå©ä½æææ²¡ä¸çé½è¦ä¸ä¸ 273 { 274 range.Add(new Range 275 { 276 from = start, 277 to = totalSize 278 }); 279 } 280 else // ä¹åçç´æ¥åå 281 { 282 range.Add(new Range 283 { 284 from = start, 285 to = start + average_size 286 }); 287 start += average_size; 288 } 289 } 290 } 291 return range; 292} 293\`\`\` 294 295## 让æå¡å¨è¿å对åºçæä»¶å 296 297å\`Response Headers\`ä¸å«ææä»¶å¤§å°ç\`Content-Length\`䏿 ·ï¼ä½ ä¹å¯ä»¥å¨\`Request Headers\`ä¸å®ä¹æ³è¦æä»¶çåªä¸é¨å 298 299å ·ä½æä½ä¸ºï¼å¨\`Request Headers\`ä¸å®ä¹ï¼ 300\`\`\` 301Range: bytes=0-63 302\`\`\` 303使ç¨C#ï¼ 304\`\`\`csharp 305Range range = new Range{from=0, to=63}; 306using (HttpClient client = new HttpClient()) 307{ 308 client.DefaultRequestHeaders.Add("Range", $"bytes={range.from}-{range.to}"); 309 using (HttpResponseMessage response = await client.GetAsync(Url)) 310 { 311 response.EnsureSuccessStatusCode(); 312 ... 313 } 314} 315\`\`\` 316 317è¿éæä»¬ä½¿ç¨**æµå¼ä¼ è¾ (Stream)**çæ¹å¼ä¸è½½æä»¶å 318 319妿ä¸ä½¿ç¨æµå¼ä¼ è¾ï¼é£ä¹æ´ä¸ªè¯·æ±ä¼è¿å ¥*åæ»*ç¶æï¼ç´å°ä¸è½½å®æ¯æ´ä¸ªæä»¶ååæ§è¡åé¢ç代ç 320 321è使ç¨äºæµå¼ä¼ è¾ï¼æä»¬å°±å¯ä»¥æ¥æ¶ä¸ç¹æä»¶ï¼å¦8192åèçæä»¶ï¼åå»å®æäºå«çä»»å¡ï¼å¦è®¡ç®ä¸è½½è¿åº¦ãä¸è½½éççï¼ï¼ä¹åéå¤è¿ä¸æ¥éª¤ç´å°æä»¶åæ¥æ¶å®æ¯ 322 323代ç å¦ä¸ï¼ 324\`\`\`csharp 325 326 327public async Task DownloadThread() 328{ 329 Range range = new Range{from=0, to=63}; // 使ç¨ä¹ååçRange 330 string UserAgent = "....." //è¿é为äºéè¿éªè¯å¡«å ¥UA 331 long pointer = range.from; 332 using (HttpClient client = new HttpClient()) 333 { 334 client.DefaultRequestHeaders.Add("Range", $"bytes={range.from}-{range.to}");//å¨Request Headersä¸å®ä¹ä¸è½½èå´ 335 client.DefaultRequestHeaders.Add("User-Agent", UserAgent); //设置UA 336 using (HttpResponseMessage response = await client.GetAsync(Url)) 337 { 338 response.EnsureSuccessStatusCode(); //ç¡®ä¿ç¶æç æ 误 339 long? totalSize = response.Content.Headers.ContentLength; //è·åè¿é¨åæä»¶åçå¤§å° 340 using (Stream contentStream = await response.Content.ReadAsStreamAsync()) //ä½¿ç¨æµå¼ä¼ è¾ 341 { 342 byte[] buffer = new byte[8192]; // æ°å»ºbufferï¼ç¨æ¥å卿µå¼ä¼ è¾æ¯æ¬¡ä¸è½½çé¨åæä»¶ 343 int bytesRead; 344 while ((bytesRead = await contentStream.ReadAsync(buffer, 0, buffer.Length)) > 0) //è·åæå¤8192åèçå 容ï¼è·åå°çå 容é¿åº¦åå ¥bytesRead 345 { 346 downloaded += bytesRead; //å·²ç»ä¸è½½å®æ¯ç大å°ï¼ç¨æ¥è®¡ç®ä¸è½½è¿åº¦ 347 lock (_lock) //使ç¨çº¿ç¨éï¼ä¹åä¼æå° 348 { 349 Console.WriteLine("WriteTo: " + pointer.ToString() + " By: " + range.from.ToString()); 350 FileStream.Seek(pointer, SeekOrigin.Begin); // å°æä»¶æéç§»å¨å°éè¦åå ¥æ°æ®çå°æ¹ 351 FileStream.Write(buffer, 0, bytesRead); // å°è·åå°çé£ç¹æ°æ®åå ¥ 352 } 353 pointer += bytesRead; 354 355 } 356 } 357 } 358 } 359} 360\`\`\` 361 362## å¤çº¿ç¨ç®¡ç 363
364ç±äºè¿æ¯ä¸ªå ¸åç**I/Oå¯éåæä½**ï¼æä»¬ä½¿ç¨å¹¶åçæ¹å¼è¿è¡å¤ç 365 366è¿éçæ³æ³æ¯ï¼ 3671. åæå¡å¨è·åæä»¶å¤§å° 3682. å建ä¸ä¸ªæä»¶å®ä¾ï¼è®©ææçº¿ç¨é½ä½¿ç¨è¿ä¸æä»¶å®ä¾è¿è¡æä»¶åå ¥æä½ 3693. è®¡ç®æ¯ä¸ªçº¿ç¨ä¸è½½çæä»¶åå¤§å° 3704. 建ç«å¤ä¸ªçº¿ç¨åæ¶ä¸è½½æä»¶ï¼ä½¿ç¨çº¿ç¨æ± èªå¨ç®¡ç 3715. çå¾ æææä»¶åä¸è½½å®æ¯ï¼æ¤æ¶ææä¸è½½çº¿ç¨å ³éï¼éæ¾æä»¶å 372 373è¿é使ç¨\`Task\`æ¥å¯¹çº¿ç¨æ± è¿è¡èªå¨ç®¡ç 374 375代ç å¦ä¸ï¼ 376\`\`\`csharp 377public async void Run() 378{ 379 long size = await GetInfo(); // è·åæä»¶å¤§å°ï¼è·å宿¯è¿å ¥ä¸ä¸æ¥ 380 Console.WriteLine("Getinfo complete, size: " + totalSize.ToString()); 381 382 // ä½ ä¹å¯ä»¥ä½¿ç¨å计æ¶å¨countDownå¯¹çº¿ç¨æ¯å¦å ¨é¨å ³éååºå¤æï¼ä½æ¯Taskæ´å ç®åå¥½ç¨ 383 //CountdownEvent countDown = new CountdownEvent(TotalThread); 384 385 List<Task> tasks = new List<Task>(); // è¿ä¸ªåè¡¨ç¨æ¥åå¨ãçå¾ ææçä¸è½½çº¿ç¨ 386 foreach (Range range in CalcRange()) // 计ç®ä¸è½½å¤§å°å¹¶æèµ·çº¿ç¨è¿è¡ä¸è½½ 387 { 388 Console.WriteLine("Thread start: " + range.from.ToString()); 389 tasks.Add(DownloadThread(range)); // å°çº¿ç¨å å ¥å表 390 } 391 await Task.WhenAll(tasks); // çå¾ ææçº¿ç¨ä¸è½½å®æ¯åéåº 392 //countDown.Wait(); 393 releaseFile(); // éæ¾æä»¶èµæº 394} 395\`\`\` 396 397## æ¹è¿ 398 399è¿éåªæ¯ä¸ä¸ªç®åç示ä¾ï¼ä½ è¿å¯ä»¥æ¹è¿è¿æ®µä»£ç ï¼å¢å é度ãè¿åº¦ç计ç®ï¼ä¸è½½æ¶çç¨åºåè° 400 401使ç¨**线ç¨é»å¡**å®ç°æåï¼**åæ¶çº¿ç¨**å®ç°åæ¶ä¸è½½æä»¶çç 402 403# æ¹è¿è¿çå¤çº¿ç¨ä¸è½½ 404 405䏿æ¯å®æä¸ç¼åçï¼æ¥æ*æåãåæ¶*çå¤çº¿ç¨ä¸è½½ 406 407é对ä¸äºç¹æ®çä¸è½½åºæ¯ï¼æå¡å¨éå¶æ¯æ¬¡åçä¸è½½å¤§å°ãè¿æ¥çº¿ç¨æ°ãééçï¼åäºä¼å 408 409## é®é¢åç¹æ®è¦æ± 410 4111. æå¡å¨éå¶äºè¿æ¥æ°ï¼æ¯ä¸ªè¿æ¥çä¸è½½å¤§å° 4122. éè¦åUI线ç¨åè° 4133. éè¦å ¼å®¹å ¶ä»çä¸è½½å¨ 4144. æå¡å¨ä¼è¿åå¤ä¸ªç¸åæä»¶çä¸è½½å°åï¼å¯ä»¥ä½¿ç¨å¤ä¸ªçº¿ç¨åå«ä¸è½½å¤ä¸ªå°åçæä»¶ 415 416 417## 使ç¨Interfaceå ¼å®¹å ¶ä»çä¸è½½å¨ 418 419> 妿æ¨çæ\`Interface\`ç使ç¨ï¼è¯·ä¸è¦å¨æ¤å¤æµªè´¹æ¶é´ 420 421ä¸ä¸ªè½¯ä»¶å¯è½ä¼æå¤ä¸ªä¸è½½å¨ï¼æ¯å¦éè¦åæ¶å ¼å®¹å线ç¨ä¸è½½ãå¤çº¿ç¨ä¸è½½ãå¤é¨ä¸è½½å¨(aria2c, motrix)çè°ç¨ 422 423å¯ä»¥é¢è§çæ¯ï¼è¿äºä¸è½½æ¹å¼å ·æçåè½æ¯é«åº¦éå¤çï¼é½å¯ä»¥ç®å为以ä¸åè½ï¼ 424 425- æ·»å ä¸è½½ä»»å¡ 426- æåä»»å¡ 427- ç»§ç»ä»»å¡ 428- å é¤ä»»å¡ 429- è·åä¸è½½è¿åº¦ 430 431对æ¤ï¼æä»¬å¯ä»¥ä½¿ç¨\`Interface\`æ¥ç®åæä»¬ç代ç ï¼ 432 433声æä¸ä¸ªInterface, å 嫿æä¸è½½æ¹å¼é½å ±æç彿° 434> å¨å£°ææ¥å£æ¶ï¼æå¥½ä½¿ç¨\`I + ç±»å\`æ¥å£°ææ¥å£å 435 436\`\`\`csharp 437public class Range 438{ 439 public long from { get; set; } 440 public long to { get; set; } 441} 442public class DownloadStatus 443{ 444 public float downloadProgress; 445 public long downloaded; 446 public long totalSize; 447 public int linkCount; 448 public int downloadingThread; 449 public long downloadBlockSize; 450 451 public bool isPaused; 452 public bool isDownloading; 453 public bool isComplete; 454} 455public interface IDownload 456{ 457 void SetCallBack(Action callBack); // UI线ç¨çç¨åºåè° 458 void AddUrl(string url); // æå¤ä¸ªUrlçè¯å¯ä»¥ç»§ç»æ·»å 459 DownloadStatus Status(); // ç¨äºè·åä¸è½½è¿åº¦ç 460 void Pause(); // æå 461 void Continue(); // ç»§ç» 462 void Cancel(); //åæ¶ 463 Task DownloadThread(Range range, string url); // ä¸è½½å½æ° 464 void releaseFile(); // éæ¾è¢«å ç¨çæä»¶ 465 void Run(); // å¼å§ä¸è½½ 466} 467\`\`\` 468 469ä¹åå建ä¸ä¸ªç±»å»ä½¿ç¨è¿ä¸ªæ¥å£ï¼ 470 471è¿éå 贴代ç ï¼è§£éå¨åé¢ 472 473\`\`\`csharp 474public class Downloader: IDownload // 声æä½¿ç¨IDownloadæ¥å£ 475{ 476 // éï¼ç¨äºä¿è¯ä¸åçº¿ç¨æ æ³åæ¶è®¿é®IOèµæº 477 private readonly object _lock = new object(); 478 // é»å¡ï¼ç¨äºæå/ç»§ç»ä¸è½½ï¼é»è®¤ç»§ç»ä¸è½½ 479 private readonly ManualResetEvent resetEvent = new ManualResetEvent(true); 480 // åæ¶ä¿¡å·ï¼å¤æç¨æ·æ¯å¦åæ¶ä»»å¡ 481 private CancellationTokenSource cts = new CancellationTokenSource(); 482 private FileStream FileStream { get; set; } 483 private int TotalThread { get; set; } 484 private int CurrentThread { get; set; } 485 private string Url { get; set; } 486 private string UserAgent { get; set; } 487 private string filePath { get; set; } 488 489 public Action? CallBack { get; set; } 490 491 private Dictionary<int, string> linkList = new Dictionary<int, string>(); 492 493 public float downloadProgress = 0; 494 public long downloaded = 0; 495 public long totalSize = 0; 496 public int linkCount = 0; 497 public int downloadingThread = 0; 498 public long downloadBlockSize = 500087; 499 500 public bool isPaused = false;
501 public bool isDownloading = true; 502 public bool isComplete = false; 503 504 505 public Downloader(string url, string user_agent, string file_path, long total_size, int thread) 506 { 507 TotalThread = thread; 508 CurrentThread = 0; 509 Url = url; 510 linkList.Add(linkCount, url); 511 linkCount++; 512 513 UserAgent = user_agent; 514 totalSize = total_size; 515 filePath = file_path; 516 517 FileStream = new FileStream(file_path, FileMode.OpenOrCreate, FileAccess.ReadWrite); 518 FileStream.SetLength(total_size); 519 } 520 public void AddUrl(string url) 521 { 522 linkList.Add(linkCount, url); 523 linkCount++; 524 525 } 526 public void SetCallBack(Action callBack) 527 { 528 CallBack = callBack; 529 } 530 private void CalcProgress() 531 { 532 if (downloaded > 0) 533 { 534 downloadProgress = (float)downloaded / totalSize; 535 } 536 if (downloadingThread == 0) 537 { 538 isDownloading = false; 539 } 540 else 541 { 542 isDownloading = true; 543 } 544 if (downloaded == totalSize) 545 { 546 isDownloading = false; 547 isComplete = true; 548 } 549 550 Console.WriteLine(downloadProgress.ToString() + "\\r"); 551 } 552 553 // å 为æå¡å¨éå¶æ¯æ¬¡ä¸è½½ç大å°ï¼æä»¥è¿éåäºä¿®æ¹ 554 // æ¯ä¸ªçº¿ç¨åªä¸è½½ downloadBlockSize 大å°çæä»¶åå°±éåº 555 private List<Range> CalcRange() 556 { 557 List<Range> ranges = new List<Range>(); 558 long calced_size = 0; 559 while (true) 560 { 561 if (calced_size + downloadBlockSize > totalSize) 562 { 563 ranges.Add(new Range 564 { 565 from = calced_size, 566 to = totalSize 567 }); 568 break; 569 570 } 571 else 572 { 573 ranges.Add(new Range 574 { 575 from = calced_size, 576 to = calced_size + downloadBlockSize, 577 }); 578 } 579 calced_size += downloadBlockSize + 1; 580 581 } 582 return ranges; 583 } 584 /// <summary> 585 /// block thread to pause 586 /// </summary> 587 public void Pause() 588 { 589 resetEvent.Reset(); 590 isPaused = true; 591 CalcProgress(); 592 CallBack?.Invoke(); 593 594 } 595 596 /// <summary> 597 /// release resetEvent to cntinue 598 /// </summary> 599 public void Continue() 600 { 601 resetEvent.Set(); 602 isPaused = false; 603 CalcProgress(); 604 CallBack?.Invoke(); 605 606 } 607 public void Cancel() 608 { 609 resetEvent.Set(); 610 isPaused = false; 611 cts.Cancel(); 612 isComplete = true; 613 CalcProgress(); 614 CallBack?.Invoke(); 615 616 } 617 public DownloadStatus Status() 618 { 619 return new DownloadStatus 620 { 621 downloadProgress = downloadProgress, 622 downloaded = downloaded, 623 totalSize = totalSize, 624 linkCount = linkCount, 625 downloadingThread = downloadingThread, 626 downloadBlockSize = downloadBlockSize, 627 628 isPaused = isPaused, 629 isDownloading = isDownloading, 630 isComplete = isComplete, 631 }; 632 } 633 public async Task DownloadThread(Range range, string url) 634 { 635 636 long pointer = range.from; 637 try 638 { 639 using (HttpClient client = new HttpClient()) 640 { 641 client.DefaultRequestHeaders.Add("Range", $"bytes={range.from}-{range.to}"); 642 client.DefaultRequestHeaders.TryAddWithoutValidation("User-Agent", UserAgent); 643 644 client.Timeout = TimeSpan.FromSeconds(10); 645 using (HttpResponseMessage response = await client.GetAsync(url)) 646 { 647 response.EnsureSuccessStatusCode(); 648 resetEvent.WaitOne(); 649 if (cts.IsCancellationRequested) 650 { 651 releaseFile(); 652 // exit thread 653 return; 654 } 655 long? totalSize = response.Content.Headers.ContentLength; 656 using (Stream contentStream = await response.Content.ReadAsStreamAsync()) 657 { 658 byte[] buffer = new byte[1000]; 659 int bytesRead; 660 while ((bytesRead = await contentStream.ReadAsync(buffer, 0, buffer.Length)) > 0) 661 { 662 resetEvent.WaitOne(); 663 if (cts.IsCancellationRequested) 664 { 665 releaseFile(); 666 // exit thread 667 return; 668 } 669 //await write 670 downloaded += bytesRead; 671 lock (_lock) 672 { 673 Console.WriteLine("WriteTo: " + pointer.ToString() + " By: " + range.from.ToString()); 674 CalcProgress(); 675 Dispatcher.UIThread.InvokeAsync(() => 676 { 677 CallBack?.Invoke(); // å¨ UI 线ç¨ä¸è°ç¨ 678 }); 679 FileStream.Seek(pointer, SeekOrigin.Begin); 680 FileStream.Write(buffer, 0, bytesRead); 681 } 682 pointer += bytesRead; 683 684 } 685 } 686 } 687 } 688 } 689 catch (Exception ex) 690 { 691 await DownloadThread(range, url); 692 } 693 694 } 695 private bool isFileReleased() { 696 try 697 { 698 return FileStream.SafeFileHandle.IsClosed; 699 } 700 catch (ObjectDisposedException) 701 { 702 return true; 703 } 704 } 705 public void releaseFile() 706 { 707 lock (_lock) 708 { 709 if (!isFileReleased()) 710 { 711 FileStream.Close(); 712 713 } 714 } 715 if (cts.IsCancellationRequested) 716 { 717 if (File.Exists(filePath)) 718 { 719 File.Delete(filePath); 720 } 721 } 722 isDownloading = false;
723 } 724 public async void Run() 725 { 726 List<Range> rangeLsit = CalcRange(); 727 SemaphoreSlim semaphore = new SemaphoreSlim(TotalThread); 728 Console.WriteLine(rangeLsit.Count.ToString()); 729 Task[] tasks = new Task[rangeLsit.Count]; 730 for (int i = 0; i < rangeLsit.Count; i++) 731 { 732 if (cts.IsCancellationRequested) 733 { 734 break; 735 } 736 await semaphore.WaitAsync(); 737 int threadId = i; 738 int arrangeLinkId = threadId % linkList.Count; 739 tasks[threadId] = Task.Run(async () => 740 { 741 try 742 { 743 downloadingThread++; 744 await DownloadThread(rangeLsit[threadId], linkList[arrangeLinkId]); 745 downloadingThread--; 746 } 747 finally 748 { 749 semaphore.Release(); 750 } 751 }); 752 } 753 if (!tasks.Contains(null)) 754 { 755 await Task.WhenAll(tasks); 756 757 } 758 releaseFile(); 759 CalcProgress(); 760 CallBack?.Invoke(); 761 762 } 763 764} 765\`\`\` 766 767## æå/ç»§ç»çå®ç° 768 769è¿é使ç¨çº¿ç¨é»å¡\`ManualResetEvent\`æ¥å®ç°ï¼ç¨æ³å\`线ç¨é\`å¾åï¼ä½ å¯ä»¥å¨[è¿é](/posts/csharp-multi-thread/#线ç¨éå线ç¨é»å¡çåºå«)æ¾å°ä»ä»¬çåºå« 770 771声æä¸ä¸ª\`ManualResetEvent\` 772\`\`\`csharp 773private readonly ManualResetEvent resetEvent = new ManualResetEvent(true); 774\`\`\` 775 776æä»¬æ¤æ¶ä¼ å ¥äº\`true\`ï¼æä»¥é»è®¤ä¸é»å¡ 777 778éè¿ä¸é¢ç彿°å¯ä»¥åæ¢æ¯å¦é»å¡ï¼ 779\`\`\`csharp 780resetEvent.Reset(); // é»å¡çº¿ç¨ 781resetEvent.Set(); // åæ¶é»å¡ 782\`\`\` 783 784ä¸é¢ç彿°å¯ä»¥å¨é»å¡æ¶æå线ç¨çè¿è¡ï¼åæ¶é»å¡æ¶æ¾è¡ï¼ 785 786\`\`\`csharp 787resetEvent.WaitOne(); 788\`\`\` 789 790æä»¥æå¨æµå¼ä¼ è¾ççå¾ æå¡å¨ç¸åºåä¸è½½é¨åæ·»å äºWaitOneæ¹æ³ï¼ 791 792\`\`\`csharp 793Some Code... 794using (HttpClient client = new HttpClient()) 795{ 796 client.DefaultRequestHeaders.Add("Range", $"bytes={range.from}-{range.to}"); 797 client.DefaultRequestHeaders.TryAddWithoutValidation("User-Agent", UserAgent); 798 799 client.Timeout = TimeSpan.FromSeconds(10); 800 using (HttpResponseMessage response = await client.GetAsync(url)) 801 { 802 response.EnsureSuccessStatusCode(); 803 resetEvent.WaitOne(); // å¦æçº¿ç¨è¢«é»å¡ï¼çº¿ç¨ä¼å¨è¿éæåè¿è¡ 804 long? totalSize = response.Content.Headers.ContentLength; 805 using (Stream contentStream = await response.Content.ReadAsStreamAsync()) 806 { 807 byte[] buffer = new byte[1000]; 808 int bytesRead; 809 while ((bytesRead = await contentStream.ReadAsync(buffer, 0, buffer.Length)) > 0) 810 { 811 resetEvent.WaitOne(); // å¦æçº¿ç¨è¢«é»å¡ï¼çº¿ç¨ä¼å¨è¿éæåè¿è¡ 812Some Code... 813\`\`\` 814 815å¹¶ç¼å\`Pause\` \`Continue\`æ¹æ³æ¥æ§å¶æ¯å¦é»å¡ï¼ 816\`\`\`csharp 817/// <summary> 818/// block thread to pause 819/// </summary> 820public void Pause() 821{ 822 resetEvent.Reset(); // é»å¡çº¿ç¨æ¥è¾¾å°æåææ 823 isPaused = true; 824 CalcProgress(); 825 CallBack?.Invoke(); 826 827} 828 829/// <summary> 830/// release resetEvent to cntinue 831/// </summary> 832public void Continue() 833{ 834 resetEvent.Set(); // åæ¶é»å¡ç»§ç»å·¥ä½ 835 isPaused = false; 836 CalcProgress(); 837 CallBack?.Invoke(); 838 839} 840\`\`\` 841 842## åæ¶ä¸è½½çå®ç° 843 844C#ä¸åå¨åæ¶ä¿¡å·\`CancellationTokenSource\`å¯ä»¥ç¨äºæ¤å¤ 845 846> è¿é¨ååºè¯¥ä¼å¨å¤çº¿ç¨è¿é¶éï¼ä½æ¯æå¤ªæäºæ²¡å»å + å¨åå¤CET4ï¼So SOON[TM] 847 848声æåæ¶ä¿¡å· 849\`\`\`csharp 850private CancellationTokenSource cts = new CancellationTokenSource(); 851\`\`\` 852 853仿¥æä¸ä¸ªå¸å°ç±»å\`IsCancellationRequested\`å¯ç¨äºæ£æµæ¯å¦è§¦ååæ¶ï¼ 854 855\`\`\`csharp 856cts.IsCancellationRequested 857\`\`\` 858 859æä»¥æä»¬å¨è¿æ¥æå¡å¨åä¸è½½æ¶æ£æµæ¯å¦åæ¶å³å¯ï¼ 860 861\`\`\`csharp 862if (cts.IsCancellationRequested) 863{ 864 releaseFile(); // å«å¿äºéæ¾è¢«å ç¨çæä»¶ 865 // ç´æ¥returnéåºçº¿ç¨å³å¯ 866 return; 867} 868\`\`\` 869 870## UIåè°çå®ç° 871 872è¿éå®ç°çæ¹å¼åæåç§ï¼æçå®ç°æ¹å¼æ¯ååå ¥æä»¶åéè¿ä¸ä¸ªCallBack彿°å®ç°å®æ¶çæ°æ®åè° 873 874\`\`\`csharp 875public Action? CallBack { get; set; } 876\`\`\` 877 878ä½¿ç¨æ¤æ¹æ³è®¾ç½®åè°å½æ°ï¼ 879\`\`\`csharp 880public void SetCallBack(Action callBack) 881{ 882 CallBack = callBack; 883} 884\`\`\` 885 886使ç¨è°åº¦å¨\`Dispatcher\`å¯ä»¥å°æä»¬ç代ç è°åº¦å°UIçº¿ç¨æ§è¡ï¼ 887 888> æ¯çï¼åæ¯ä¸ä¸ªåºè¯¥å¨å¤çº¿ç¨è¿é¶éåºç°çä¸è¥¿ï¼æçå以ååå¡« 889 890äºæ¯å¨åå ¥æä»¶ç代ç ä¹åå ä¸ï¼ 891 892\`\`\`csharp 893Dispatcher.UIThread.InvokeAsync(() => 894{ 895 CallBack?.Invoke(); // å¨ UI 线ç¨ä¸è°ç¨ 896}); 897\`\`\` 898 899ç°å¨æ¯æ¬¡ä¸è½½å®æä»¶åå°±ä¼è°ç¨CallBack彿°äºï¼å¨CallBack彿°ä¸æ·»å ç¶ææ¾ç¤ºçé»è¾ä»£ç å³å¯ 900 901## ä¼å 902 903è¿é¨å代ç 仿以ä¸é®é¢ 904### ç±äºç½ç»ç¯å¢å¾å·®ï¼æä»¶éæ¾ä¸åæ¶ 905
906è¿é¨ååå å ¶å®ä¸æ¯ä»£ç 导è´çï¼èæ¯å¾å¤ä¸è½½æå¡å¨ä¼**éé** 907 908ééçåççç®åçæ¬æ¯ï¼ 909 910ä¸è½½ç第ä¸ç§ä¼ è¾æ°æ®ï¼å9ç§ä¸ä¼ è¾æ°æ®ï¼å¦æ¤å¾ªç¯ 911 912ç»è¿éåº¦ç®æ³çæ©çï¼å¯ä»¥ä½¿\`æ¾ç¤ºçä¸è½½é度\`æ´å å¹³æ»ï¼ææå·²ç»æ¯\`0B/s\`使¯æ¾ç¤º\`100KB/s\`ï¼æ®é人å¾é¾å¯è§å° 913 914ä¸ä¸ªå¾ç®åçæ£éªæ¹æ³ï¼æå¼ä»»å¡ç®¡çå¨ï¼ççç½å¡å ç¨çå¾è¡¨ï¼å¦æççåä¸åº§ä¸åº§å±±ï¼å°±æ¯è¢«ééäº 915 916è§£å³æ¹æ³ï¼ 917 918- æ·»å æ´å¤çåæ¶ä¿¡å·çæµç¹ 919- åæ¶ä»»å¡åç´æ¥éæ¾æä»¶ï¼å¹¶å¨åå ¥æä»¶åæ·»å \`FileStream\`æ¯å¦å¯ç¨çæ£æµ 920 921### 妿ç½ç»åºéï¼ä¸è½½ä¼ç¯çéè¯è¿å ¥æ»å¾ªç¯ 922 923è§£å³æ¹æ³ï¼ 924- æ·»å æå¤§éè¯æ° 925 926## ç»å°¾ 927 928æå¥½ç´¯... 929`;export{n as default};
Line numbers count LF bytes from the start of the resource, as the search results do. Vendor segments are library code the classifier recognised; they are stored but not indexed. Bytes are shown as Latin1 characters, one per byte.