在windows切换线程之前强制完成对c#方法的异步调用

在windows切换线程之前强制完成对c#方法的异步调用,c#,multithreading,streamwriter,parallel.for,C#,Multithreading,Streamwriter,Parallel.for,我有一个项目,它使用Parallel.For调用立即执行文件的批量修改,因为它们都需要相当长的时间,我希望在修改过程中提供反馈。整个过程还将备份更改的文件,并在执行过程中创建一个撤消批处理命令。撤消文件必须输出撤消步骤并刷新撤消文件缓冲区,然后再执行任何其他任务,以便在更改文件的过程实际开始之前保存所有批处理步骤(复制原始文件和删除副本)。例如如果我要更改两个文件“A.bin”“B.bin”,我希望批处理文件显示: copy "A.original" "A.bin&

我有一个项目,它使用Parallel.For调用立即执行文件的批量修改,因为它们都需要相当长的时间,我希望在修改过程中提供反馈。整个过程还将备份更改的文件,并在执行过程中创建一个撤消批处理命令。撤消文件必须输出撤消步骤并刷新撤消文件缓冲区,然后再执行任何其他任务,以便在更改文件的过程实际开始之前保存所有批处理步骤(复制原始文件和删除副本)。例如如果我要更改两个文件“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切换到其他线程之前,是否有方法标记/强制完成方法或块?从我对async/await的理解来看,这是一个不同的用例,无法实现我所需要的(这是我在网上查找如何执行类似操作时可以找到的唯一搜索结果)

下面是将步骤实际添加到批处理文件的代码。整个方法必须在没有线程开关的情况下完全执行:

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时间,那么最终操作系统仍将接管并让其他线程运行
  • 线程在请求无法立即提供的内容时,基本上会放弃时间片。最常见的例子是I/O,这正是您的场景。当你要求操作系统处理磁盘时,你的线程会说“好吧,我现在已经完成了……当你得到我想要的东西时,请告诉我。”无论线程的优先级如何,操作系统都会挂起线程,让另一个线程运行
  • 您的问题没有足够的细节,无法确切了解场景是什么。但你至少有两个选择:

  • 如果您的工作有较小的操作组,这些操作组需要相互一致,但不需要在所有操作中一致(即与其他操作组一致),那么只需将这些组作为并发的粒度。然后可以保证每个组内的操作顺序
  • 如果所有的操作都被认为是相互一致的,并且您仍然希望它们被同时处理,那么就将它们视为一个“事务”。这在并发场景中很常见,因为可能发生的故障会使系统处于不一致/损坏的状态。特别是:添加某种状态,例如用作标记的文件,指示操作已开始,并仅在整个操作完成时删除该状态
  • 在您的示例中,第二个选项可能涉及将所有副本作为单个事务执行,然后仅当第一个事务完成时,将所有文件删除作为第二个事务执行。这样,复制操作期间的故障将使系统处于可以恢复的状态(继续复制或回滚到以前的状态),并且删除操作期间的故障也可以以类似方式处理

    最后,我将指出,如果所有文件操作都发生在同一台设备上,那么使用并发可能没有什么好处。根据代码实际执行的操作,单个线程可能能够使设备保持与两个或多个线程一样的繁忙状态。毕竟,即使是SSD也比CPU慢得多。添加更多线程可能只会造成一种情况,即所有线程都在一个设备上相互争斗,不会产生任何加速。在某些情况下,它甚至可能会减慢速度


    因此,最好的解决方案可能是完全放弃整个并发方面。您应该向自己证明,多线程解决方案的测量性能实际上比单线程解决方案快得多,足以证明额外的复杂性。

    在AddCommit中首先将所有命令写入字符串,然后只执行一个UndoScript,最后执行WriteLine(希望您在UndoScript中使用锁,以便多个线程不能同时写入文件)您熟悉该语句吗?请注意,多线程处理很困难,并提供了许多令人兴奋的新方法来打击您自己!将所有命令写入stringbuilder可以解决眼前的问题。