繁体   English   中英

C#任务等待和超时

[英]C# Task wait and timeout

我试图使用以下技术,以便能够执行某些操作的工作任务,超时10秒并且不会阻止应用程序。

internal void ReadAll()
{
    var data = new byte[1];

    Task.Factory.StartNew(() =>
    {
        var ct = new CancellationTokenSource();
        var ReadAllTask = Task.Factory.StartNew(() =>
        {
            // Read all information
            // [omit communication exchange via COM port]

            ct.Cancel();
        }, ct.Token);

        // First thread waiting 10s for the worker to finish
        ReadAllTask.Wait(10000, ct.Token);

        if (ReadAllTask.Status == TaskStatus.RanToCompletion)
        {
            ReadAllComplete?.Invoke(true);
        }
        else
        {
            ct.Cancel();
            ReadAllComplete?.Invoke(false);
        }
    });
}

按下按钮可调用此方法。 在我看来,在调试配置工作正常,但不是在发布配置中“第一个线程”永远不会到达等待并且没有抛出任何事件。

您的代码可能比当前版本简单得多。 为事件创建非阻塞方法的最简单方法是使用async关键字标记它,并使用await关键字从SerialPort.BaseStream属性启动异步读取操作

此外, CancellationTokenSource可以随着时间创建,之后它会自动取消,而取消的正确方法是调用CancellationToken.ThrowIfCancellationRequested方法。 async/await机制将在UI上下文中调用事件,因此代码可能是这样的:

// async void is a recommended way to use asynchronous event handlers
private async void btnReadAll_Click(object sebder, EventArgs e)
{
    var data = new byte[2];

    // cancel source after 10 seconds
    var cts = new CancellationTokenSource(10000);
    // Read all information
    // [omit communication exchange via COM port]
    // async operation with BaseStream
    var result = await SerialPort.BaseStream.ReadAsync(data, 0, 2, cts.Token);

    /*
     * if you can't use the BaseStream methods, simply call this method here
     * cts.Token.ThrowIfCancellationRequested();
    */

    // this code would run only if everything is ok

    // check result here in your own way
    var boolFlag = result != null;

    ReadAllComplete?.Invoke(boolFlag);
}

这里只是一个快速重写,用于删除事件并将异步IO中的同步IO API包装起来。 如果可能的话,您应该切换到真正的异步API并删除Task.Run

    private CancellationTokenSource cts;

    public async void MyButtonhandler(object sender, EventArgs e) {
        cts = new CancellationTokenSource();
        try {
            var result = await Task.Run(() => ReadAll(cts));
            if (result) {
                //success
            } else {
                //failure
            }
        } catch (TaskCanceledException ex) {

        }            
    }

    internal async Task<bool> ReadAll(CancellationTokenSource cts) {
        byte[] data = new byte[1];
        var timeout = TimeSpan.FromSeconds(10);

        var ReadAllTask = Task.Run(() => {
            // Read all information
            // [omit communication exchange via COM port]

        }, cts.Token);

        if (await Task.WhenAny(ReadAllTask, Task.Delay(timeout)) == ReadAllTask) {
            return true;
        }
        cts.Cancel();
        return false;
    }

阅读我的问题的评论和答案我学到了几个解决我的问题的有用的东西:

  • CancellationTokenSource可以具有隐式超时
  • 使用Task.Run而不是Task.Factory.StartNew
  • 不需要取消任务,cts将完成工作

现在我的代码更简单,它的工作原理:

private void Read_All_Button_Click(object sender, RoutedEventArgs e)
{
    // Start timedout task that will send all necessary commands
    CancellationTokenSource cts = new CancellationTokenSource(10000);
    Task.Run(() =>
    {
        oCommandSets.ReadAll(cts);
    }, cts.Token);
}

internal void ReadAll(CancellationTokenSource cts)
{
     // [communication]

     if (cts.IsCancellationRequested)
     {
         ReadAllComplete?.Invoke(false);
     }
     else
     {
         ReadAllComplete?.Invoke(true);
     }
}

无论如何,我需要了解有关多线程的更多信息。

暂无
暂无

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM