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

问题描述

我有一个使用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这样的抢占式多任务操作系统的整个前提是,该进程对线程的上下文切换基本上没有控制权。

最接近的是调整线程优先级。在某些情况下,与控制上下文切换相似,但由于以下两个原因,甚至不能完全控制流程:

  1. 线程调度包括缓解线程饥饿的方法。具有提升优先级的线程将优先使用一段时间,但是如果这样做使其他线程的CPU时间紧缺,则最终OS仍将接管并让其他线程运行。
  2. 当线程请求无法立即提供的内容时,它们实质上会放弃其时间片。最常见的示例是I / O,这正是您的情况。当您要求操作系统对磁盘进行某些操作时,您的线程说:“好,我现在就完成了……让我知道您什么时候想要的。”无论线程的优先级是什么,操作系统都会挂起该线程并让另一线程运行。

您的问题没有足够的详细信息,无法确切了解情况是什么。但是您至少有两个选择:

  1. 如果您的工作中有较小的一组操作需要相互一致,但不需要在所有操作之间保持一致(即与其他操作组保持一致),则只需将这些组作为并发的粒度即可。然后,您可以保证每个组中的操作顺序。
  2. 如果所有操作都应该是相互一致的,而您仍然希望同时进行处理,则将其视为“事务”。这在并发方案中很常见,在这种情况下,可能发生的故障会使系统处于不一致/损坏的状态。具体来说:添加某种状态,例如用作标记的文件,指示该操作已开始,并仅在整个操作完成后才删除该状态。

在您的示例中,第二个选项可能涉及将所有副本作为一个事务进行,然后仅在第一个事务完成时才将所有文件删除作为第二个事务进行。这样,复制操作期间的故障将使系统处于可以恢复的状态(继续复制或回滚到先前的状态),并且删除操作期间的故障同样可以得到处理。>

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

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

相关问答

错误1:Request method ‘DELETE‘ not supported 错误还原:...
错误1:启动docker镜像时报错:Error response from daemon:...
错误1:private field ‘xxx‘ is never assigned 按Alt...
报错如下,通过源不能下载,最后警告pip需升级版本 Requirem...