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

使用Task.WhenAny()结合Task.Delay()未触发超时问题排查

Task.WhenAny搭配Task.Delay在Windows 10 22H2平板上无法触发超时的问题解决

我遇到一个BLE相关的AsyncOperation挂起问题,为添加超时处理实现了标准Task.WhenAny模式,但在Windows 10 22H2平板上,当BLE任务挂起后,Task.Delay(20000)无法按时完成,导致程序永久挂起、无法进入超时分支;开发PC上运行完全正常。即使改用System.Runtime.WindowsRuntime内置的AsTask()方法,问题依旧存在。

原实现代码

GattDeviceServicesResult services = null;
var task = bleDevice.GetGattServicesAsync(BluetoothCacheMode.Uncached).AsTask();

if (await Task.WhenAny(task, Task.Delay(20000)) == task)
{
    Console.WriteLine($"Found Services for device:");    

    services = task.Result;
    if (services.Services != null)
    {
        foreach (var service in services.Services)
        {
            Console.WriteLine($"Service: {service.Uuid}");
        }
    }
}
else
{
    Console.WriteLine("Timed Out while getting service list.");
}

自定义AsTask实现(问题复现时也试过系统内置版本)

public static Task<TResult> AsTask<TResult>(this IAsyncOperation<TResult> operation)
{
    var tcs = new TaskCompletionSource<TResult>();

    operation.Completed += delegate
    {
        switch(operation.Status)
        {
            case AsyncStatus.Completed:
                tcs.TrySetResult(operation.GetResults());
                break;
            case AsyncStatus.Error:
                tcs.TrySetException(operation.ErrorCode);
                break;
            case AsyncStatus.Canceled:
                tcs.SetCanceled();
                break;
        }
    };

    return tcs.Task;
}

问题分析

该问题大概率和Windows 10 22H2版本中BLE相关的线程调度异常有关:当原BLE操作挂起时,可能占用了UI线程或特定同步上下文,导致Task.Delay的回调无法被正常触发,进而超时逻辑失效。

解决方案

方案1:用CancellationToken主动取消原操作

给超时逻辑绑定CancellationToken,超时后主动取消BLE操作,避免资源占用,同时确保超时分支能执行:

GattDeviceServicesResult services = null;
using var cts = new CancellationTokenSource(20000);
var bleOperation = bleDevice.GetGattServicesAsync(BluetoothCacheMode.Uncached);
var task = bleOperation.AsTask(cts.Token);

try
{
    services = await task;
    Console.WriteLine($"Found Services for device:");    
    if (services.Services != null)
    {
        foreach (var service in services.Services)
        {
            Console.WriteLine($"Service: {service.Uuid}");
        }
    }
}
catch (OperationCanceledException)
{
    Console.WriteLine("Timed Out while getting service list.");
    bleOperation.Cancel(); // 主动取消原AsyncOperation释放资源
}
catch (Exception ex)
{
    Console.WriteLine($"Error getting services: {ex.Message}");
}

方案2:脱离同步上下文执行超时逻辑

使用ConfigureAwait(false)避免绑定当前同步上下文,确保Task.Delay能在后台线程正常触发:

GattDeviceServicesResult services = null;
var task = bleDevice.GetGattServicesAsync(BluetoothCacheMode.Uncached).AsTask();

var completedTask = await Task.WhenAny(task, Task.Delay(20000)).ConfigureAwait(false);
if (completedTask == task)
{
    services = await task.ConfigureAwait(false);
    Console.WriteLine($"Found Services for device:");    
    if (services.Services != null)
    {
        foreach (var service in services.Services)
        {
            Console.WriteLine($"Service: {service.Uuid}");
        }
    }
}
else
{
    Console.WriteLine("Timed Out while getting service list.");
}

方案3:使用WinRT原生超时机制

部分WinRT异步操作支持直接设置Timeout属性,利用原生机制处理超时:

GattDeviceServicesResult services = null;
var bleOperation = bleDevice.GetGattServicesAsync(BluetoothCacheMode.Uncached);
bleOperation.Timeout = 20000; // 针对支持Timeout属性的WinRT操作

try
{
    services = await bleOperation.AsTask();
    Console.WriteLine($"Found Services for device:");    
    if (services.Services != null)
    {
        foreach (var service in services.Services)
        {
            Console.WriteLine($"Service: {service.Uuid}");
        }
    }
}
catch (Exception ex)
{
    if (ex.HResult == unchecked((int)0x800705B4)) // WinRT超时错误码
    {
        Console.WriteLine("Timed Out while getting service list.");
    }
    else
    {
        Console.WriteLine($"Error getting services: {ex.Message}");
    }
}

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.06.14 11:01:11