简体   繁体   English

C#任务等待和超时

[英]C# Task wait and timeout

I am trying to use the following technique to be able to have a worker task executing some operations, with a 10 sec timeout and without blocking the application. 我试图使用以下技术,以便能够执行某些操作的工作任务,超时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);
        }
    });
}

This method is called by pressing a button. 按下按钮可调用此方法。 It seems to me that in debug configuration works properly, but not in release configuration where the "first thread" never reach the wait and no event is thrown. 在我看来,在调试配置工作正常,但不是在发布配置中“第一个线程”永远不会到达等待并且没有抛出任何事件。

Your code could be a lot simpler than current version. 您的代码可能比当前版本简单得多。 Easiest way to make a non-blocking method for event is to mark it with async keyword and use the await keyword to start the asynchronous read operation from SerialPort.BaseStream property. 为事件创建非阻塞方法的最简单方法是使用async关键字标记它,并使用await关键字从SerialPort.BaseStream属性启动异步读取操作

Also, CancellationTokenSource could be created with time, after that it get cancelled automatically, and the right way to cancel is to call CancellationToken.ThrowIfCancellationRequested method. 此外, CancellationTokenSource可以随着时间创建,之后它会自动取消,而取消的正确方法是调用CancellationToken.ThrowIfCancellationRequested方法。 async/await mechanism will invoke the event in UI context, so code could be something like this: 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);
}

Here's just a quick rewrite to remove the event and wrap what appears to be a synchronous IO API in an async one. 这里只是一个快速重写,用于删除事件并将异步IO中的同步IO API包装起来。 If at all possible you should switch to a true async API and drop the Task.Run . 如果可能的话,您应该切换到真正的异步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;
    }

Reading comments and answers to my question I learned a couple of useful things that solve my problem: 阅读我的问题的评论和答案我学到了几个解决我的问题的有用的东西:

  • CancellationTokenSource can have an implicit timeout CancellationTokenSource可以具有隐式超时
  • use Task.Run instead Task.Factory.StartNew 使用Task.Run而不是Task.Factory.StartNew
  • don't need to cancel the task, the cts will do the work 不需要取消任务,cts将完成工作

Now my code is simpler and it works: 现在我的代码更简单,它的工作原理:

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);
}

and

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

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

In any case I need to learn more about multithreading. 无论如何,我需要了解有关多线程的更多信息。

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

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