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

如何使用CancellationToken实现异步方法超时退出?

解决CancellationToken实现网络挂载超时取消的问题

你的核心问题在于NetworkDrive.MapNetworkDrive是同步阻塞方法,它不会主动响应CancellationToken的取消信号,普通的ThrowIfCancellationRequested()只能在调用瞬间检查一次,无法中断后续的阻塞操作。下面给出两种可行的实现方案:

方案一:基于Task.WhenAny的超时返回(兼容原有同步API)

这种方案能实现超时后立即返回,但无法中断正在运行的同步挂载线程(因为.NET已弃用不安全的线程终止方式),线程会自行运行至结束。

修改后的方法代码

// 给CancellationToken添加扩展方法,用于监听取消信号
public static class CancellationTokenExtensions
{
    public static Task WhenCanceled(this CancellationToken cancellationToken)
    {
        var tcs = new TaskCompletionSource<bool>();
        cancellationToken.Register(s => ((TaskCompletionSource<bool>)s).SetResult(true), tcs);
        return tcs.Task;
    }
}

internal static async Task<bool> PerformTargetLocationMounting(string address, string drive, CancellationToken ct)
{
    Console.WriteLine("[*] Attempting target mounting");

    // 将同步挂载操作包装到Task中
    var mountTask = Task.Run(() =>
    {
        try
        {
            NetworkDrive.MapNetworkDrive(drive, address);
            return true;
        }
        catch (Exception ex)
        {
            Console.WriteLine($"[!] Mounting error: {ex.Message}");
            return false;
        }
    }, ct);

    // 等待挂载完成或取消信号触发
    var completedTask = await Task.WhenAny(mountTask, ct.WhenCanceled());

    if (completedTask == mountTask)
    {
        // 挂载任务正常完成,返回结果
        return await mountTask;
    }
    else
    {
        // 超时/取消触发,返回失败
        Console.WriteLine("[!] Mounting operation timed out or canceled");
        return false;
    }
}

关键点说明

  • Task.Run(..., ct):传入CancellationToken仅标记任务为取消状态,不会中断同步操作。
  • Task.WhenAny:确保我们在挂载完成或取消触发时立即返回,避免主线程被无限阻塞。
  • 局限性:如果超时取消,执行挂载的后台线程仍会继续运行,直到自身完成(比如网络超时报错),但不会影响主线程逻辑。

方案二:调用系统命令实现可中断的异步挂载(更优)

如果允许替换NetworkDrive.MapNetworkDrive,推荐调用系统net use命令,通过异步进程操作实现真正的取消(超时后杀死进程)。

实现代码

internal static async Task<bool> PerformTargetLocationMounting(string address, string drive, CancellationToken ct)
{
    Console.WriteLine("[*] Attempting target mounting");

    using var process = new Process
    {
        StartInfo = new ProcessStartInfo
        {
            FileName = "net",
            Arguments = $"use {drive}: {address}",
            RedirectStandardOutput = true,
            RedirectStandardError = true,
            UseShellExecute = false,
            CreateNoWindow = true
        }
    };

    // 注册取消回调:超时后立即杀死进程
    using var cancelRegistration = ct.Register(() =>
    {
        if (!process.HasExited)
        {
            try
            {
                process.Kill();
            }
            catch { } // 忽略杀死进程时的异常
        }
    });

    try
    {
        process.Start();
        // 异步等待进程退出,同时响应取消信号
        await process.WaitForExitAsync(ct).ConfigureAwait(false);

        if (process.ExitCode == 0)
        {
            return true;
        }
        else
        {
            var errorMsg = await process.StandardError.ReadToEndAsync().ConfigureAwait(false);
            Console.WriteLine($"[!] Mount failed with exit code {process.ExitCode}: {errorMsg}");
            return false;
        }
    }
    catch (OperationCanceledException)
    {
        Console.WriteLine("[!] Mounting operation canceled due to timeout");
        return false;
    }
    catch (Exception ex)
    {
        Console.WriteLine($"[!] Mounting error: {ex.Message}");
        return false;
    }
}

优势

  • 真正可中断:超时后直接杀死挂载进程,避免后台线程残留。
  • 自带错误信息:通过net use的错误输出可以更精准定位挂载失败原因。

你的调用方代码无需修改,cts.CancelAfter(2000)的逻辑完全适配上述两种方案。

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.08.05 01:11:31