问题描述
我有一组嵌套的方法,可以有效地复制文件以进行备份,然后使用“ RecordConsolidator”类对该文件进行更改。在事件链中,我得到了文件正在使用的随机异常,除非对Parallel.For调用中的每一行代码都是异步执行的,否则对我而言这是没有意义的。这是我当前问题的示例(在注释行中):
// this is from a method called CompactFormIDs
try
{
// will this ever be executed twice on the same object? Will it be
// released before the next line of code after the catch statement?
File.Copy(dest,source);
}
catch (Exception e)
{
weirdExceptions.Add(source); // I keep getting a message that the file already exists
// even though there is only one copy statement which copies the file above.
}
// creates an undo step in a batch file
Globals.AddCommit(CommitType.RestoreBackup | CommitType.UndoDeleteBackup | CommitType.CommitDeleteBackup,source,dest);
HashSet<FormID> npcList = new HashSet<FormID>();
uint mask = (uint)masters.Count << 24;
report.BeginAppendProcess();
// the method below also causes an exception.
// It acts as though the file copied is still in use. No other process accesses the file other than the
// copy process before this statement. When not doing Parallel.For this works just fine.
using (RecordConsolidator consolidator = new RecordConsolidator(source,dest,mask,npcList))
{...
}
最终目标是:
-
制作要修改的文件副本,以便在新版本的文件无法正常工作时将其还原。
-
将原始文件的还原添加到批处理脚本中
-
对文件进行更改。
如何在使用Parallel.For方法的并行过程中执行此操作,而不会遇到所有这些“正在使用/存在的文件”问题。这里甚至没有问题的事实是没有意义的,因为单个Copy语句会导致多个问题,除非复制在执行其余代码之前未完成或以某种方式执行Parallel.For两次,否则不应发生多个问题。每个项目。
更新1:这是包含Parallel.For循环的方法:
private void OnFormShown(object sender,EventArgs e)
{
Mod mod;
RichTextboxBuilder builder;
List<Mod> batch = task as List<Mod>;
Refresh();
if (batch != null)
{
RichTextboxBuilder.BeginConcurrentAppendProcess(this,batch.Count);
ReportCaption = "Conversion Progress";
progressBar.Visible = true;
progressBar.Maximum = batch.Count;
Parallel.For(0,batch.Count,i =>
{
mod = batch[i];
builder = RichTextboxBuilder.BeginConcurrentAppend(i);
//builder.TextUpdated += Builder_TextUpdated;
taskTarget.ConvertToESL(mod,builder,false);
RichTextboxBuilder.EndConcurrentAppend(i);
});
Finalize(false);
}
else
{
mod = task as Mod;
ReportTextBuilder = new RichTextboxBuilder(this);
Finalize(taskTarget.ConvertToESL(mod,ReportTextBuilder));
}
}
解决方法
如评论中所述,我使用了线程安全的HashSet
var fileQueue = new HashSet<string>(StringComparer.Ordinal);
您可以使用Lock()或通过ReaderWriterLockSlim对其进行管理以使其具有线程安全性。
我面临的另一个问题是“我不是一个人在服务器上”,这意味着其他进程在做其他事情,这让我知道,但有时我没有把整个服务器都交给我自己;-)
看看Nuget包Walter
它具有扩展方法
TryDiscoverWhoisBlocking(this FileInfo file,out IReadOnlyList<Process> processes)
我可以在此处粘贴代码,但是有很多本机方法,并且发布时间会太长。
使用该方法查看谁阻止了您对该文件的访问,这可能是病毒扫描程序,如果是这样,您会收到错误,然后循环一会儿,让病毒扫描程序执行其操作,直到所有句柄都被清除为止。文件,您可以继续。
我发现大多数时候“我是问题”和“我是阻止访问的人”,所以我看一下我的代码和图,为什么在实际上有一种方法可以使用这些单行代码的情况下按照我希望的方式进行操作。
同胞,我告诉我我认为需要的阻止类型,并且只接受我需要的阻止级别。在您的情况下,您可以无限制地打开共享读和写功能,也可以使用...无论如何选择。
using (var fs = new FileStream(path: file.FullName,access: FileAccess.Write,mode: FileMode.Append,share: FileShare.ReadWrite))
using (var sw = new StreamWriter(fs,encoding: UTF8Encoding.UTF8))
{
sw.Write(text);
sw.Flush();
}
我正在写这篇文章,因为我猜您的代码可以正常工作,并且锁定是由另一个线程或另一个进程引起的,上面的代码将明确告诉您发生了什么事情。
,Walter的代码绝对可以帮助其他人解决此问题。但是,我的问题(以及伴随它发生而引起的不一致)是Windows消息重复发送到消息循环的结果。如果OnFormShown消息出现两次,它将两次调用消息处理程序。此修改解决了这个特定问题:
我添加了一个“处理过的”类变量,并将添加到原始帖子中的处理程序更改为以下内容。 (添加Task.Run是为了解决另一个问题)。
private void OnFormShown(object sender,EventArgs e)
{
if (!processed)
{
processed = true;
Mod mod;
RichTextboxBuilder builder;
List<Mod> batch = task as List<Mod>;
Refresh();
if (batch != null)
{
RichTextboxBuilder.BeginConcurrentAppendProcess(this,batch.Count);
ReportCaption = "Conversion Progress";
progressBar.Visible = true;
progressBar.Maximum = batch.Count;
Task.Run(() =>
{
Parallel.For(0,batch.Count,i =>
{
mod = batch[i];
builder = RichTextboxBuilder.BeginConcurrentAppend(i);
//builder.TextUpdated += Builder_TextUpdated;
taskTarget.ConvertToESL(mod,builder,false);
RichTextboxBuilder.EndConcurrentAppend(i);
}); Finalize(false);
});
}
else
{
mod = task as Mod;
ReportTextBuilder = new RichTextboxBuilder(this);
Finalize(taskTarget.ConvertToESL(mod,ReportTextBuilder));
}
}
}