C# 在锁内使用线程池和线程完成更新
在几篇关于线程化应用程序开发的教程之后,我实现了一个小的实践实用程序,它使用Task.Run和C# 在锁内使用线程池和线程完成更新,c#,threadpool,C#,Threadpool,在几篇关于线程化应用程序开发的教程之后,我实现了一个小的实践实用程序,它使用Task.Run和Threadpool.QueueUserWorkItem递归索引文件夹并生成包含的所有文件的校验和清单 虽然应用程序运行良好,但在专门使用线程池的部分中,我无法更新UI。当使用Task.Run时,使用waitApplication.Current.Dispatcher.BeginInvoke可以正确更新UI 所需的行为是当每个线程池的线程完成各自的任务时,在ProcessChecksums中继续更新进度
Threadpool.QueueUserWorkItem
递归索引文件夹并生成包含的所有文件的校验和清单
虽然应用程序运行良好,但在专门使用线程池的部分中,我无法更新UI。当使用Task.Run时,使用waitApplication.Current.Dispatcher.BeginInvoke
可以正确更新UI
所需的行为是当每个线程池的线程完成各自的任务时,在ProcessChecksums
中继续更新进度
int queuedThreads = 0;
object locker = new object();
CancellationTokenSource cancellationTokenSource;
List<string> lstFilePaths = new List<string>();
Manifest manifestData;
event Action<double> OnProgressChange;
event Action<string> OnStatusChange;
async void GenerateManifest(Object sender, RoutedEventArgs e) {
status = "Indexing Files";
progress = 1;
cancellationTokenSource = new CancellationTokenSource();
await Task.Run(() => Async_GenerateManifest(cancellationTokenSource.Token), cancellationTokenSource.Token);
if (!cancellationTokenSource.Token.IsCancellationRequested) {
Finished();
}else{
Cancelled();
}
}
async Task Async_GenerateManifest(CancellationToken cancellationToken) {
if (cancellationToken.IsCancellationRequested) { return; }
Async_IndexFiles(initialpath, cancellationToken);
//Works Perfectly
await Application.Current.Dispatcher.BeginInvoke(DispatcherPriority.Background, new Action(() => {
OnStatusChange("Generating Checksums");
OnProgressChange(10);
}));
ProcessChecksums(cancellationToken);
//Works Perfectly
await Application.Current.Dispatcher.BeginInvoke(DispatcherPriority.Background, new Action(() => {
OnStatusChange("Saving Manifest");
OnProgressChange(90);
}));
SaveManifestFile(cancellationToken);
}
void ProcessChecksums(CancellationToken cancellationToken) {
List<FileData> lstFileData = new List<FileData>();
for (int i = 0; i < lstFilePaths.Count; i++) {
if (cancellationToken.IsCancellationRequested) { return; }
string s = lstFilePaths[i];
lock (locker) queuedThreads++;
ThreadPool.QueueUserWorkItem( x => {
manifestData.AddFileData(new FileData(s, GenerateChecksum(s)));
});
}
lock (locker) {
while (queuedThreads > 0) {
if (cancellationToken.IsCancellationRequested) { return; }=
Monitor.Wait(locker);
//Can't use await Dispatcher.BeginInvoke as is inside a lock, doesn't update GUI while waiting.
OnProgressChange((((queuedThreads - lstFilePaths.Count) * -1) / lstFilePaths.Count) - 0.2);
}
}
}
string GenerateChecksum(string filePath) {
//Time-consuming checksum generation
//...
lock (locker) {
queuedThreads--;
Monitor.Pulse(locker);
}
return BitConverter.ToString(checksum);
}
int-queuedThreads=0;
对象锁定器=新对象();
CancellationTokenSource CancellationTokenSource;
List lstfilepath=新列表();
舱单数据;
事件-行动-改变;
对状态变化的事件动作;
异步void GenerateManifest(对象发送方,RoutedEventArgs e){
status=“索引文件”;
进展=1;
cancellationTokenSource=新的cancellationTokenSource();
等待任务。运行(()=>Async_GenerateManifest(cancellationTokenSource.Token),cancellationTokenSource.Token);
如果(!cancellationTokenSource.Token.IsCancellationRequested){
完成的();
}否则{
取消();
}
}
异步任务async_GenerateManifest(CancellationToken CancellationToken){
if(cancellationToken.IsCancellationRequested){return;}
异步索引文件(initialpath、cancellationToken);
//完美地工作
等待应用程序.Current.Dispatcher.BeginInvoke(DispatcherPriority.Background,新操作(()=>{
OnStatusChange(“生成校验和”);
变化(10);
}));
处理校验和(取消令牌);
//完美地工作
等待应用程序.Current.Dispatcher.BeginInvoke(DispatcherPriority.Background,新操作(()=>{
OnStatusChange(“保存舱单”);
变化(90);
}));
SaveManifestFile(cancellationToken);
}
无效处理校验和(CancellationToken CancellationToken){
List lstFileData=新列表();
对于(int i=0;i{
AddFileData(新文件数据,GenerateChecksum);
});
}
锁(储物柜){
while(queuedThreads>0){
if(cancellationToken.IsCancellationRequested){return;}=
监视器。等待(储物柜);
//无法使用Wait Dispatcher.BeginInvoke,因为它位于锁中,在等待时不更新GUI。
OnProgressChange(((queuedThreads-lstfilePath.Count)*-1)/lstfilePath.Count)-0.2);
}
}
}
字符串GenerateChecksum(字符串文件路径){
//耗时的校验和生成
//...
锁(储物柜){
队列线程--;
监视器。脉冲(锁定器);
}
返回BitConverter.ToString(校验和);
}
使用后台线程的进度更新更新UI的标准模式是使用IProgress
/progress
。与直接使用Dispatcher
相比,更现代的方法有几个好处
// You'd want to set these while on the UI thread.
// E.g., in your constructor:
// _percentProgress = new Progress<double>(value => ...);
// _statusProgress = new Progress<string>(value => ...);
IProgress<double> _percentProgress;
IProgress<string> _statusProgress;
async void GenerateManifest(Object sender, RoutedEventArgs e) {
status = "Indexing Files";
progress = 1;
cancellationTokenSource = new CancellationTokenSource();
await Task.Run(() => GenerateManifest(cancellationTokenSource.Token));
if (!cancellationTokenSource.Token.IsCancellationRequested) {
Finished();
}else{
Cancelled();
}
}
void GenerateManifest(CancellationToken cancellationToken) {
if (cancellationToken.IsCancellationRequested) { return; }
Async_IndexFiles(initialpath, cancellationToken);
_statusProgress?.Report("Generating Checksums");
_percentProgress?.Report(10);
ProcessChecksums(cancellationToken);
_statusProgress?.Report("Saving Manifest");
_percentProgress?.Report(90);
SaveManifestFile(cancellationToken);
}
void ProcessChecksums(CancellationToken cancellationToken) {
List<FileData> lstFileData = new List<FileData>();
for (int i = 0; i < lstFilePaths.Count; i++) {
if (cancellationToken.IsCancellationRequested) { return; }
string s = lstFilePaths[i];
lock (locker) queuedThreads++;
ThreadPool.QueueUserWorkItem( x => {
manifestData.AddFileData(new FileData(s, GenerateChecksum(s)));
});
}
lock (locker) {
while (queuedThreads > 0) {
if (cancellationToken.IsCancellationRequested) { return; }
Monitor.Wait(locker);
_percentProgress?.Report((((queuedThreads - lstFilePaths.Count) * -1) / lstFilePaths.Count) - 0.2);
}
}
}
//您可能希望在UI线程上设置这些。
//例如,在您的构造函数中:
//_percentProgress=新进度(值=>…);
//_statusProgress=新进度(值=>…);
i进展(百分比进展);;
i进展(i)状态进展;;
异步void GenerateManifest(对象发送方,RoutedEventArgs e){
status=“索引文件”;
进展=1;
cancellationTokenSource=新的cancellationTokenSource();
wait Task.Run(()=>GenerateManifest(cancellationTokenSource.Token));
如果(!cancellationTokenSource.Token.IsCancellationRequested){
完成的();
}否则{
取消();
}
}
void GenerateManifest(CancellationToken CancellationToken){
if(cancellationToken.IsCancellationRequested){return;}
异步索引文件(initialpath、cancellationToken);
_statusProgress?报告(“生成校验和”);
_进度百分比?报告(10);
处理校验和(取消令牌);
_statusProgress?报告(“保存清单”);
_进度百分比?报告(90);
SaveManifestFile(cancellationToken);
}
无效处理校验和(CancellationToken CancellationToken){
List lstFileData=新列表();
对于(int i=0;i{
AddFileData(新文件数据,GenerateChecksum);
});
}
锁(储物柜){
while(queuedThreads>0){
if(cancellationToken.IsCancellationRequested){return;}
监视器。等待(储物柜);
_percentProgress?.Report(((queuedThreads-lstfilepath.Count)*-1)/lstfilepath.Count)-0.2);
}
}
}
代码还有一些其他问题QueueUserWorkItem
可能应替换为Task。运行和等待任务。当所有发生异常时,确保不会中断进程。如果(…)完成();否则取消()将返回类型更改为Task
,可能最能代表code>。使用throwifccancellationrequested
而不是IsCancellationRequested
。还有一种监视器。没有脉冲的等待
,只是用于进度更新,这很奇怪;允许代码报告自己的进度会更干净。如果您单独查看其中的每一个,您应该会发现您的代码更干净。使用后台线程的进度更新更新UI的标准模式是使用IProgress
/