C # return result

I have read a lot about how to return a value for a task, but I cannot get it to work on my code and still produces System.Threading.ThreadAbortException

.

Tried using Task.WaitAll

it though it might block the UI but to no avail.

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.

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

      

DoTask

is just a method that checks and queries the databases.

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


      

+3


source to share


1 answer


I think it's time for you to take a step towards async / await. It will make your life so much easier.

Somewhere in your code, you want to run multiple tasks and wait for all tasks to complete without blocking your UI. In your example, this is GetResult.

You want GetResult to return a DataTable object. If you want to use async await, you declare the GetResult function and return the task, for example:

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

      

You usually name your async functions with the word async at the end

In this function, you can start tasks, perform other tasks while those tasks are running, and wait for the tasks to complete. This waiting is called "wait".

You can only wait for a Task or Task object, so you are expecting a function that returns Task.



Task.WaitAll does not return Task, but void. Therefore, you cannot wait for Task.Waitall.

Better to wait for Task.WhenAll. This function returns a task and you can expect it.

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 a discussion of why they currently prefer Task.Run

This function will give you one result. If you want to call if, you will need to make the caller function asynchronous as well and return the task or task to it. Its caller must also be async and so on, until you get to the event handler. This is the only one who can return the 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 stuff etc. It is replaced by a wait, followed by the next statement, where the result of the task is available in Task.Result.

Remember, if you run Task.WhenAll and one of the tasks you are waiting for throws an exception, you get an AggregateException in which all exceptions thrown by all tasks are grouped into the InnerExceptions property. So if you want, you can catch the AggregateException and look at all innerexceptions to see which task threw those exceptions.

0


source







All Articles