C#任务等待和超时

时间:2017-03-07 15:30:48

标签: c# wpf multithreading task-parallel-library

我正在尝试使用以下技术让一个工作人员任务执行某些操作,超时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);
        }
    });
}

按下按钮调用此方法。在我看来,在调试配置工作正常,但不是在发布配置,其中"第一个线程"永远不会等待,也不会抛出任何事件。

3 个答案:

答案 0 :(得分:2)

您的代码可能比当前版本简单得多。为事件制作非阻止方法的最简单方法是使用async关键字对其进行标记,并使用await关键字从asynchronous read operation属性启动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);
}

答案 1 :(得分:1)

这里只是一个快速重写,用于删除事件并将异步IO中的同步IO API包装起来。如果可能的话,您应该切换到真正的异步API并删除con = new OracleConnection(); con.ConnectionString = "..."; con.Open(); //<--- do this

Task.Run

答案 2 :(得分:0)

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

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

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