4

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.

Tried using Task.WaitAll even though this 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;
    }
}

The DoTask is just a method that checks and query 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'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");
    });

8
  • Why are you wanting to use a Task in this scenario? Commented Jul 2, 2015 at 1:38
  • Please see updated code above. Commented Jul 2, 2015 at 1:46
  • Please add that code to your original post so that it is properly formatted and everyone can see it as part of the question. Commented Jul 2, 2015 at 1:47
  • Does Task.Wait() work for you? Commented Jul 2, 2015 at 2:00
  • 1
    I'm pretty sure you're aborting the thread from the outside. While this code is a terrible approach to using Tasks, there's no opportunity for a Thread.Abort from the inside. Try searching your whole code base for Thread.Abort. Also, you do realize you're never returning the result, right? Commented Jul 2, 2015 at 7:26

1 Answer 1

1

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.

You want GetResult to return an object of DataTable. If you want to use async await, you declare the function GetResult and return a Task, like this:

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

It is quite common to name your async functions with the word async at the end

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.WaitAll doesn't return Task, but void. So you can't await for Task.Waitall.

Better is to await for Task.WhenAll. That function returns a Task, and thus you can await for 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 discussions why they nowadays prefer 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. Its caller should also be async etc. until you get to the event handler. This is the only one who may return 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. It is replace by await, followed by the next statement where the result of the task is available in 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. So if you want you can catch the AggregateException and foreach all innerexceptions to see which task threw which exceptions.

Sign up to request clarification or add additional context in comments.

1 Comment

Your GetResultAsync() async method doesn't compile and gives the error: Not all code paths return a value (I realize that this is years later, though it seems like a great answer but just needs some tweaking)

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.