[英]Asynchronously copy large files while reporting progress to a progress bar (example?)
我需要復制一些大文件,並通過進度條將進度報告回UI。
我購買了“ C#5.0”。 我在第597頁。我一直在閱讀有關並行編程的文章。 根據本書中的一些示例,我試圖完成我認為很簡單的事情,但是我真的很努力。 我的理解上一定有差距。 這就是為什么我要發布此問題。
我調查了后台工作人員,但是在嘗試獲得進展時發現自己遇到了跨線程編譯器錯誤。
我研究了異步命令,但發現自己對lambda表達式有誤解。 那或如何通過單擊按鈕異步地實際執行任務的代碼,同時仍將進度報告回UI線程。
我在MSDN上的代碼項目中傾注了許多現有的問題/答案,在這里我自己問了幾個問題,並否決了它們。 我只需要一個簡單的例子,我就可以全神貫注,我會順利進行的。
我確信我的答案是在異步,Task.Run,File.Copy(可能是StreamReader / StreamWriter類)和IProgress中。 在我為期兩周的研究和反復試驗中發現的問題/答案要么不完整,要么對於某些給定場景而言過於寬泛/過於具體。
我只需要一個帶有進度條的UI的工作示例,以及一個在新線程中執行代碼的按鈕即可復制一組大文件(或僅一個大文件)並報告進度。 從那里,我可以使用它並根據自己的需要進行調整,以進一步提高我的整體理解。
根據克林特的答案改編的代碼,但仍無法正確更新進度
這種調整會在異步任務中復制文件,但僅在復制文件后才將進度從0更新到100%。 因為我正在處理大文件,所以基於文件數量的處理進度還不夠。
到目前為止,我沒有發現或嘗試過地址在大文件的字節進度%-age的更新過程中異步執行復制的地址。
using System;
using System.Collections.Generic;
using System.ComponentModel;
using System.Data;
using System.Drawing;
using System.Linq;
using System.Text;
using System.Windows.Forms;
using System.Threading.Tasks;
using System.IO;
namespace CopyProgressWorking
{
public partial class Form1 : Form
{
public Form1()
{
InitializeComponent();
string srcFile = @"C:\temp\BigFile.txt";
string dstFile = @"C:\temp\temp2\BigFile.txt";
button1.Click += (s, e) => DoCopy(srcFile, dstFile);
}
public async Task CopyFiles(Dictionary<string, string> files, Action<int> progressCallback)
{
for (var x = 0; x < files.Count; x++)
{
var item = files.ElementAt(x);
var from = item.Key;
var to = item.Value;
using (var outStream = new FileStream(to, FileMode.Create, FileAccess.Write, FileShare.Read))
{
using (var inStream = new FileStream(from, FileMode.Open, FileAccess.Read, FileShare.Read))
{
long size = inStream.Position;
Console.WriteLine("Filesize is {0}", size);
await inStream.CopyToAsync(outStream);
}
}
progressCallback((int)((x + 1) / files.Count) * 100);
}
}
public async void DoCopy(string srcFile, string dstFile)
{
label1.Text = "Copying " + srcFile;
await CopyFiles(new Dictionary<string, string>
{
{srcFile, dstFile}
},
prog =>
{
Invoke((MethodInvoker)delegate {
progressBar1.Value = prog;
if (prog >= 100)
{
label1.Text = "Copy complete!";
}
});
});
}
}
}
這應該可以幫助您入門:
public static class Copier
{
public static async Task CopyFiles(Dictionary<string,string> files, Action<int> progressCallback)
{
for(var x = 0; x < files.Count; x++)
{
var item = files.ElementAt(x);
var from = item.Key;
var to = item.Value;
using(var outStream = new FileStream(to, FileMode.Create, FileAccess.Write, FileShare.Read))
{
using(var inStream = new FileStream(from, FileMode.Open, FileAccess.Read, FileShare.Read))
{
await inStream.CopyToAsync(outStream);
}
}
progressCallback((int)((x+1)/files.Count) * 100);
}
}
}
public class MyUI
{
public MyUI()
{
copyButton.Click += (s,e) => DoCopy();
}
public async void DoCopy()
{
await Copier.CopyFiles(new Dictionary<string,string>
{
{"C:\file1.txt", "C:\users\myuser\desktop\file1.txt"},
{"C:\file2.txt", "C:\users\myuser\desktop\file2.txt"}
}, prog => MyProgressBar.Value = prog);
}
}
這是在沒有Visual Studio的情況下手動編寫的,因此可能存在一些問題(拼寫等)。
核心概念是:
基本上所有這些代碼所做的是:
示例類“ MyUI”只是winforms或WPF窗口的簡化版本,帶有一個將其全部關閉的按鈕(在click事件處理程序中)和進度條。
無需啟動新線程,您可以保留TPL(任務並行化庫)來為您處理調度,盡管通常示例中的所有代碼通常都在UI線程上運行。 這不是問題,因為異步/等待“魔術”可確保在復制操作期間不會阻止UI線程。
IProgress非常適合一種更通用的機制,在這種機制中,您需要從調用層次結構中深入報告,如果您僅上一層(如我的示例),則簡單的回調就足夠了。
使用字典只是為了說明問題,實際上,您可能希望使用IEnumerable<Tuple<string,string>>
或IEnumerable<MyTypeHere>
表示所需的操作。
超級基本的逐字節復制
// Assuming you've got a from and to file stream open
// here is some hand-written pseudocode (C# style) to show the basic concept
foreach(var file in files)
{
var from = OpenFromStream(file.From);
var to = OpenFromStream(file.To);
var lengthOfFile = from.Length;
for(x = 0; x < lengthOfFile; x++)
{
to.WriteByte(from.ReadByte());
progress((int)(x / lengthOfFile) * 100);
}
}
這是一些超級簡單的偽代碼,用於說明逐字節復制和報告文件進度的基礎。
聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.