简体   繁体   English

C#任务返回输出

[英]C# Task Return output

I've read a lot on how to return value for a task but I can't seem to get it to work on my code and still produces the System.Threading.ThreadAbortException . 我已经阅读了很多关于如何为任务返回值的内容,但我似乎无法让它处理我的代码并仍然产生System.Threading.ThreadAbortException

Tried using Task.WaitAll even though this might block the UI but to no avail. 使用Task.WaitAll尝试即使这可能会阻止UI但无济于事。

public DataTable GetResult(SomeVariable someVariable) {
    // this do not work
    //var task = Task<DataTable>.Factory.StartNew(() =>
    var task = Task.Factory.StartNew<DataTable>(() =>
    {
        DataTable matchedData = new DataTable();
        matchedData = DoTask(someVariable);
        return matchedData;
    }, TaskCreationOptions.LongRunning);
    try
    {
        var allTasks = new Task[] { task };
        Task.WaitAll(allTasks);
        return task.Result as DataTable;
    }
    catch (ArgumentException)
    {
        throw;
    }
    catch (Exception)
    {
        // Always get the exception  here: "A first chance exception of type 'System.Threading.ThreadAbortException' occurred in mscorlib.dll"
        throw;
    }
}

Tried using ContinueWhenAll but still the same. 尝试使用ContinueWhenAll但仍然相同。

public DataTable GetResultV2(SomeVariable someVariable)
{
    queue = new Queue<Task>();
    DataTable matchedData = new DataTable();
    var task = Task.Factory.StartNew(() =>
    {
        matchedData = DoTask(someVariable);
        return matchedData;
    }, TaskCreationOptions.LongRunning);
    queue.Enqueue(task);
    try
    {
        var done = Task.Factory.ContinueWhenAll(queue.ToArray(), completed =>
            {
                return matchedData;
            });
        return done.Result as DataTable;
    }
    catch (ArgumentException)
    {
        throw;
    }
    catch (Exception)
    {
        // Always get the exception  here: "A first chance exception of type 'System.Threading.ThreadAbortException' occurred in mscorlib.dll"
        throw;
    }
}

The DoTask is just a method that checks and query databases. DoTask只是一种检查和查询数据库的方法。

private DataTable DoTask(SomeVariable someVariable)
{
    DataTable matchedData = new DataTable();
    // long database process/query
    // populate and return matchedData
    return matchedData;
}

edit: For reference on how/why it's being used. 编辑:有关如何/为何使用它的参考。



    foreach (DataRow row in data.Rows)
    {
        string columnName = Convert.ToString(row["columnName"]);
        string ProjectName = Convert.ToString(row["ProjectName"]);
        string dbase_group = Convert.ToString(row["dbase_group"]);
        string dbase_data = Convert.ToString(row["dbase_data"]);
        var task = Task.Factory.StartNew(() =>
            {
                SomeVariable someVariable = new SomeVariable();
                someVariable.DbName = dbase_group;
                someVariable.columnName = columnName;
                someVariable.ProjectName = ProjectName;
                someVariable.TblName = dbase_data;
                using (SearchProject search = new SearchProject())
                {
                    DataTable result = new DataTable();
                    result = search.GetResult(SomeVariable);
                }
            });
        queue.Enqueue(task);
    }
    Task.Factory.ContinueWhenAll(queue.ToArray(), ant =>
    {
        Console.WriteLine("Done with all tasks");
    });

I think it is time for you to take the step to async/await. 我认为现在是时候采取步骤异步/等待了。 This will make your live much easier. 这将使您的生活更轻松。

Somewhere in your code you want to start several tasks and wait for all tasks to complete without blocking your user interface. 在代码中的某个位置,您希望启动多个任务并等待所有任务完成,而不会阻止您的用户界面。 In your example this is GetResult. 在您的示例中,这是GetResult。

You want GetResult to return an object of DataTable. 您希望GetResult返回DataTable的对象。 If you want to use async await, you declare the function GetResult and return a Task, like this: 如果要使用async await,则声明函数GetResult并返回一个Task,如下所示:

public async Task<DataTable> GetResultAsync(SomeVariable someVariable) {...}

It is quite common to name your async functions with the word async at the end 在末尾用async命名异步函数是很常见的

In this function you can start tasks, do other things while these tasks are running and wait for the tasks to finish. 在此功能中,您可以启动任务,在这些任务运行时执行其他操作并等待任务完成。 This waiting is called "await". 这种等待被称为“等待”。

You can only await for a Task or a Task object, so you await for a function that returns Task. 您只能等待Task或Task对象,因此您需要等待返回Task的函数。

Task.WaitAll doesn't return Task, but void. Task.WaitAll不返回Task,但是void。 So you can't await for Task.Waitall. 所以你不能等待Task.Waitall。

Better is to await for Task.WhenAll. 更好的是等待Task.WhenAll。 That function returns a Task, and thus you can await for it. 该函数返回一个Task,因此您可以等待它。

public async Task<DataTable> GetResultAsync(SomeVariable someVariable)
{
    var task = Task.Run( () =>
    {
        DataTable matchedData = new DataTable();
        matchedData = DoTask(someVariable);
        return matchedData;
    }
}

If you want you can still use Task.Factory.StartNew, see MSDN for discussions why they nowadays prefer Task.Run 如果你想要你仍然可以使用Task.Factory.StartNew,请参阅MSDN讨论为什么他们现在更喜欢Task.Run

This function will get you one result. 这个功能可以得到一个结果。 If you want to call if, you'll have to make the caller function also async and let it return Task or Task. 如果要调用if,则必须使调用者函数也为异步,并让它返回Task或Task。 Its caller should also be async etc. until you get to the event handler. 它的调用者也应该是异步等,直到你到达事件处理程序。 This is the only one who may return void: 这是唯一可以返回void的人:

private async void OnButton1_clicke(object Sender, ...)
{
    try
    {
        await ProcessAllInputsAsync(...)
    }
    catch (ArgumentException exc)
    {
        ProcessArgumentException(...)
    }
    catch (Exception exc)
    {
         ProcessOtherException(...)
    }           
}

// first example: no parallel processing:
private async Task ProcessAllInputsAsync(...)
{   
    foreach (SomeVariable someVariable in GetSomeVariables(...))
    {
        DataTable dataTable = await GetResultAsync(...);
        ProcessDataTable(dataTable);
    }
}

// or do parallel processing: start several tasks and wait until all ready:
private async Task ProcessAllInputsAsync(...)
{ 
    List<Task<DataTable>> tasks = new List<Task<DataTable>>();  
    foreach (SomeVariable someVariable in GetSomeVariables(...))
    {
        tasks.Add(GetResultAsync(someVariable);
    }
    // all tasks are started, await for all to finish:
    await Task.WhenAll(tasks.ToArray());
    // REMEMBER: you can only await for a Task or Task<T>
    // Task.WhenAll returns a Task, so you can await for it
    // Task.WaitAll returns void, so you can't await for it.

    // now that all tasks are finished, get the results:
    // Each Task<TResult> has the result in property Task.Result
    // The result of a Task<TResult> is a TResult:
    IEnumerable<TResult> fetchedDataTables = tasks.Select(task => task.Result);

    // you can process it here if you want or do other things with it:
    foreach (DataTabe fetchedDataTable in fetchedDataTables)
    {
        ProcessFetchedDataTable(fetchedDataTable);
    }
}

See that you got rid of all ContinueWith etc stuff. 看到你摆脱了所有的ContinueWith等东西。 It is replace by await, followed by the next statement where the result of the task is available in Task.Result. 它被替换为await,然后是下一个语句,其中任务的结果在Task.Result中可用。

Be aware: if you do a Task.WhenAll, and one of the tasks you are waiting for throws an exception, you get an AggregateException where all exceptions thrown by all tasks are grouped in property InnerExceptions. 请注意:如果您执行Task.WhenAll,并且您正在等待的任务之一抛出异常,则会出现AggregateException,其中所有任务抛出的所有异常都归入属性InnerExceptions。 So if you want you can catch the AggregateException and foreach all innerexceptions to see which task threw which exceptions. 因此,如果您需要,您可以捕获AggregateException并预先知道所有的innerexceptions,以查看哪个任务引发了哪些异常。

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

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