You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

C#异步文件复制中如何实现同步暂停以处理用户交互?

在C#中实现可暂停并等待用户输入的异步文件复制流程

需求背景

  • 基于async/await/Task<T>实现异步文件复制,全程不阻塞UI线程
  • 当目标位置已存在待复制文件时,需在主线程显示模态对话框询问用户操作(覆盖/跳过/全部覆盖/全部跳过等)
  • 多复制任务需逐个处理此类交互场景,避免弹窗混乱

现有方案分析

你提出的拆分Task、保存状态、遇到交互时终止当前Task并重启的思路是可行的,但存在状态管理繁琐、流程拆分碎片化的问题——每次重启都需要恢复到之前的执行节点,代码复杂度会随着流程分支增加而上升。

更优实现方案:异步等待用户输入模式

核心思路是不终止正在运行的Task,而是让Task暂停并异步等待用户的决策,同时确保UI操作在主线程执行。使用TaskCompletionSource<T>可以优雅地实现这种“等待外部输入”的异步逻辑,结合UI框架的线程调度器(如WPF的Dispatcher、WinForms的Control.Invoke)保证弹窗在主线程显示。

代码实现示例

首先定义必要的枚举和状态类:

public enum FileConflictAction
{
    Overwrite,
    Skip,
    OverwriteAll,
    SkipAll
}

public class FileCopyState
{
    public string SourcePath { get; set; }
    public string DestinationPath { get; set; }
    public long TotalBytesCopied { get; set; }
    public FileConflictAction? GlobalConflictAction { get; set; }
}

主复制流程实现:

// 多任务场景下可通过队列逐个调用此方法
public async Task ExecuteSingleFileCopyAsync(FileCopyState state, IProgress<long> copyProgress)
{
    // 如果已有全局决策,直接应用无需弹窗
    if (state.GlobalConflictAction.HasValue)
    {
        await ProcessFileWithDecision(state, state.GlobalConflictAction.Value, copyProgress);
        return;
    }

    // 检查目标文件是否存在
    if (File.Exists(state.DestinationPath))
    {
        // 异步等待用户决策,确保弹窗在UI线程显示
        var userAction = await AskUserForConflictResolution(state);
        
        // 记录全局决策(如果用户选择了"全部覆盖/跳过")
        if (userAction is FileConflictAction.OverwriteAll or FileConflictAction.SkipAll)
        {
            state.GlobalConflictAction = userAction;
        }

        await ProcessFileWithDecision(state, userAction, copyProgress);
    }
    else
    {
        await CopyFileToDestination(state, copyProgress);
    }
}

// 核心:异步等待用户输入的方法
private async Task<FileConflictAction> AskUserForConflictResolution(FileCopyState state)
{
    var tcs = new TaskCompletionSource<FileConflictAction>();

    // WPF环境下使用Dispatcher确保UI操作在主线程执行
    Application.Current.Dispatcher.Invoke(() =>
    {
        var dialog = new FileConflictDialog(state.SourcePath, state.DestinationPath);
        bool? dialogResult = dialog.ShowDialog();
        
        tcs.SetResult(dialogResult == true ? dialog.SelectedAction : FileConflictAction.Skip);
    });

    // WinForms环境下替换为Control.Invoke:
    // yourProgressWindow.Invoke(() => { ... });

    return await tcs.Task;
}

// 实际执行文件复制的异步方法
private async Task CopyFileToDestination(FileCopyState state, IProgress<long> copyProgress)
{
    const int bufferSize = 4096;
    using var sourceStream = new FileStream(state.SourcePath, FileMode.Open, FileAccess.Read, FileShare.Read, bufferSize, true);
    using var destStream = new FileStream(state.DestinationPath, FileMode.Create, FileAccess.Write, FileShare.None, bufferSize, true);

    byte[] buffer = new byte[bufferSize];
    int bytesRead;
    while ((bytesRead = await sourceStream.ReadAsync(buffer)) > 0)
    {
        await destStream.WriteAsync(buffer.AsMemory(0, bytesRead));
        state.TotalBytesCopied += bytesRead;
        copyProgress.Report(state.TotalBytesCopied);
    }
}

// 根据用户决策处理文件
private async Task ProcessFileWithDecision(FileCopyState state, FileConflictAction action, IProgress<long> copyProgress)
{
    switch (action)
    {
        case FileConflictAction.Overwrite:
        case FileConflictAction.OverwriteAll:
            await CopyFileToDestination(state, copyProgress);
            break;
        case FileConflictAction.Skip:
        case FileConflictAction.SkipAll:
            // 跳过当前文件,可记录跳过日志
            break;
    }
}

方案优势

  • 流程连续:无需拆分Task,整个复制流程在同一个Task内完成,状态管理更自然
  • 异步等待:通过TaskCompletionSource实现非阻塞等待,Task暂停但不终止,避免重复初始化流程
  • 线程安全:通过UI框架的调度器确保弹窗在主线程显示,符合UI线程模型要求
  • 扩展性强:轻松支持全局决策、多任务队列等需求,代码结构清晰易维护

多任务场景处理

如果是批量复制任务,只需将所有FileCopyState放入一个队列,然后逐个执行ExecuteSingleFileCopyAsync即可:

public async Task ExecuteBatchCopyAsync(List<FileCopyState> copyTasks, IProgress<long> totalProgress)
{
    long totalBytes = copyTasks.Sum(t => new FileInfo(t.SourcePath).Length);
    long copiedBytes = 0;

    foreach (var task in copyTasks)
    {
        await ExecuteSingleFileCopyAsync(task, new Progress<long>(bytes =>
        {
            copiedBytes = task.TotalBytesCopied;
            totalProgress.Report(copiedBytes * 100 / totalBytes);
        }));
    }
}

内容的提问来源于stack exchange,提问作者Spook

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.08.01 12:05:20