我必须在哪里抛出异常以防止在任务中无休止地等待

Where must i throw an exception to prevent endless waiting in Task

我有一个硬件设备通过串行端口连接到我的 PC,当我向它发送信号时,它会发回信号 "Hi, here I am!",然后成功检测到它所连接的端口。我用 C# 写了一个等待硬件设备响应的任务,但是如果它没有连接,那么这个任务就会永远等待。我在哪里可以抛出异常来防止这种无休止的等待?

我的代码:

public static Task<string> GetDevicePortName()
{
    // Get all available serial ports on system.
    var ports = SerialPort.GetPortNames();
    var serialPort = new SerialPort();

    serialPort.BaudRate = Constants.DeviceConstants.BaudRate;
    serialPort.Parity = Constants.DeviceConstants.SerialPortParity;
    serialPort.StopBits = Constants.DeviceConstants.SerialPortStopBits;
    serialPort.WriteTimeout = Constants.DeviceConstants.WriteTimeoutInMilliseconds;

    var taskCompletionSource = new TaskCompletionSource<string>();
    serialPort.DataReceived += (s, e) => 
    {
        var dataIn = (byte)serialPort.ReadByte();
        var receivedCharacter = Convert.ToChar(dataIn);

        if (receivedCharacter == Constants.DeviceConstants.SignalYes)
        {
            serialPort.Dispose();
            taskCompletionSource.SetResult(serialPort.PortName);
        }
    };

    foreach (var port in ports)
    {
        serialPort.PortName = port;

        try
        {
            serialPort.Open();
            serialPort.Write(Constants.DeviceConstants.SignalDeviceDetect);
        }
        catch (IOException e) { }
    }

    return taskCompletionSource.Task;
}

您可以创建一个 "Custom timeout" 组合 Task.WhenAnyTask.Delay:

public async Task GetDevicePortNameAsync()
{
    var cts = new CancellationTokenSource();
    var timeOutTask = Task.Delay(5000, cts.Token);
    var deviceNameTask = GetDevicePortName(cts.Token);

    var finishedTask = await Task.WhenAny(timeOut, deviceNameTask);
    if (finishedTask == timeOutTask)
    {
        // You've timed-out
    }
    // If you get here, the deviceName is available.
}

请注意,这 不会 取消对 SerialPort 的基础注册。

编辑:

@KDecker 添加了一个传递 CancellationToken 的想法,如果我们在返回 TaskCompletionSource.Task 之前已经超时,可以对其进行监控。它看起来像这样:

public static Task<string> GetDevicePortName(CancellationToken cancellationToken)
{
    // Get all available serial ports on system.
    var ports = SerialPort.GetPortNames();
    var serialPort = new SerialPort();

    serialPort.BaudRate = Constants.DeviceConstants.BaudRate;
    serialPort.Parity = Constants.DeviceConstants.SerialPortParity;
    serialPort.StopBits = Constants.DeviceConstants.SerialPortStopBits;
    serialPort.WriteTimeout = Constants.DeviceConstants.WriteTimeoutInMilliseconds;

    var taskCompletionSource = new TaskCompletionSource<string>();
    serialPort.DataReceived += (s, e) => 
    {
        var dataIn = (byte)serialPort.ReadByte();
        var receivedCharacter = Convert.ToChar(dataIn);

        if (receivedCharacter == Constants.DeviceConstants.SignalYes)
        {
            serialPort.Dispose();
            taskCompletionSource.SetResult(serialPort.PortName);
        }
    };

    foreach (var port in ports)
    {
        if (cancellationToken.IsCancellationRequested)
        {
            // Unregister from serialPort, and clean up whatever needs to be cleaned
            taskCompletionSource.SetResult(null);
            break;
        }

        serialPort.PortName = port;

        try
        {
            serialPort.Open();
            serialPort.Write(Constants.DeviceConstants.SignalDeviceDetect);
        }
        catch (IOException e) { }
        finally
        {
            serialPort.Dispose();
        }
    }

    return taskCompletionSource.Task;
}