问题描述
我有一个使用Parallel.For调用的项目,它可以立即执行文件的批量修改,因为它们都需要花费大量时间,并且我想在修改过程中提供反馈。整个过程还将备份更改的文件,并在执行过程中创建一个undo batch命令。撤消文件必须必须输出撤消步骤并刷新撤消文件缓冲区,然后再执行任何其他任务,以便在更改过程之前保存所有批处理步骤(复制原始文件并删除副本)。该文件实际上开始。例如...如果我要更改两个文件“ A.bin”和“ B.Bin”,我希望该批处理文件说:
copy "A.original" "A.bin"
delete "A.original"
copy "B.original" "B.bin"
delete "B.original"
问题在于,异步调用可以在并行调用之间切换,该方法可以生成上面的输出,该方法创建具有以下输出的文件:
copy "A.original" "A.bin"
copy "B.original" "B.bin"
delete "A.original"
delete "B.original"
这会导致一种情况,如果在每个步骤中程序崩溃或文件之间的处理出现问题,则“撤消”脚本最终会留下“删除”行,从而使撤消批处理留下需要删除的垃圾文件删除。
在Windows切换到另一个线程之前,是否有办法标记/强制执行某个方法或块?据我对异步/等待的了解,这有一个不同的用例,无法满足我的需要(这是我在网上查找如何进行此类操作时可以找到的唯一搜索结果)。
这是将步骤实际添加到批处理文件中的代码。整个方法必须在没有线程切换的情况下完全执行:
internal static void AddCommit(CommitType type,string sourcePath,string destPath = null)
{
if ((type & CommitType.RestoreBackup) != 0)
{
if (destPath == null)
throw new ArgumentNullException();
UndoScript.WriteLine("copy \"" + sourcePath + "\" \"" + destPath + "\" /y");
}
if ((type & CommitType.UndoDeleteBackup) != 0)
UndoScript.WriteLine("delete \"" + sourcePath + "\" /q");
if ((type & CommitType.CommitDeleteBackup) != 0)
CommitScript.WriteLine("delete \"" + sourcePath + "\" /q");
UndoScript.Flush();
if (CommitScript != null)
CommitScript.Flush();
}
解决方法
您的问题中有很多东西,很难准确地理解您对答案的期望。就是说,实际上只有一个明确的问题:
在Windows切换到另一个线程之前,是否有办法标记/强制执行某个方法或块?
这很容易回答:否。
像Windows这样的抢占式多任务操作系统的整个前提是,该进程对线程的上下文切换基本上没有控制权。
最接近的是调整线程优先级。在某些情况下,与控制上下文切换相似,但由于以下两个原因,甚至不能完全控制流程:
- 线程调度包括缓解线程饥饿的方法。具有提升优先级的线程将优先使用一段时间,但是如果这样做使其他线程的CPU时间紧缺,则最终OS仍将接管并让其他线程运行。
- 当线程请求无法立即提供的内容时,它们实质上会放弃其时间片。最常见的示例是I / O,这正是您的情况。当您要求操作系统对磁盘进行某些操作时,您的线程说:“好,我现在就完成了……让我知道您什么时候想要的。”无论线程的优先级是什么,操作系统都会挂起该线程并让另一线程运行。
您的问题没有足够的详细信息,无法确切了解情况是什么。但是您至少有两个选择:
- 如果您的工作中有较小的一组操作需要相互一致,但不需要在所有操作之间保持一致(即与其他操作组保持一致),则只需将这些组作为并发的粒度即可。然后,您可以保证每个组中的操作顺序。
- 如果所有操作都应该是相互一致的,而您仍然希望同时进行处理,则将其视为“事务”。这在并发方案中很常见,在这种情况下,可能发生的故障会使系统处于不一致/损坏的状态。具体来说:添加某种状态,例如用作标记的文件,指示该操作已开始,并仅在整个操作完成后才删除该状态。
在您的示例中,第二个选项可能涉及将所有副本作为一个事务进行,然后仅在第一个事务完成时才将所有文件删除作为第二个事务进行。这样,复制操作期间的故障将使系统处于可以恢复的状态(继续复制或回滚到先前的状态),并且删除操作期间的故障同样可以得到处理。>
最后,我要指出的是,如果所有文件操作都在同一设备上进行,那么使用并发可能几乎没有好处。根据代码实际执行的操作,单个线程可能能够使设备保持几乎与两个或更多线程一样繁忙。毕竟,即使是SSD也比CPU慢得多。添加更多线程可能只是在造成一种情况,即所有线程在一个设备上相互争斗,而没有提速。在某些情况下,它甚至可能使速度变慢。
因此,最好的解决方案可能是完全放弃整个并发方面。您应该向自己证明,多线程解决方案的测量性能实际上比单线程解决方案快得多,足以证明额外的复杂性。