Using Task.Factory.StartNew() Then Return Value

1.4k views Asked by At

I have a class that has a few tasks that are created and occur. When I run my test app an example of the threads created/used are as follows...

  • Thread9 (Main Thread)
  • Thread9
  • Thread9
  • Thread10 (Worker StartNew Thread)
  • Thread11 (Worker ContinueWith Thread)
  • Thread11

I'm able to pass an object from my main thread(9) into a worker thread(10) and from the worker thread into the worker ContinueWith thread(11) [AKA the completion of the worker thread(10)]. Everything I do works great with one exception... I can't seem to get that object back to the main thread using something like an event. Whenever I try to delegate or Invoke back to the main thread it will not work. It always fires on Thread11 instead of Thread9!

My goal is to get the object (being used in Thread11) back to the main Thread9.

Notes:

  • I can not use .Wait() or pause the MainUI in any way.
  • I can not use Task.WaitAll() or Task.WaitAny() as it will freeze the UI too.
  • Invoke will not work because this is a custom class and not a form.

UPDATED - Example:

private SQLConnectionProperties sql = new SQLConnectionProperties();

private void SQLTest()
{
    //Being called from main UI thread 9
    sql.SQLErrorMessage = "FIRST CHANGE";
    Task<SQLConnectionProperties> taskConnect = Task.Factory
        .StartNew(() => threadConnect(sql), CancellationToken.None, TaskCreationOptions.None, TaskScheduler.Default)
        .ContinueWith((task) => threadConnect(task.Result, true));
    //Continue back to main UI (still in main UI thread 9)
}

private SQLConnectionProperties threadConnect(SQLConnectionProperties sql, bool bContinuation = false)
{
    if (!bContinuation)
    {
        //In new thread 10
        sql.SQLErrorMessage = "SECOND CHANGE";
    }
    else
    {
        //In new thread 11
        sql.SQLErrorMessage = "THIRD CHANGE";
    }
    return sql;
}

Using the above code I would have everything I want up until the In new thread 11 line. After SQLErrorMessage is changed to "THIRD CHANGE" and it gets returned I need some type of event to be raised or something to get the results back to the main UI thread 9.

2

There are 2 answers

0
Arvo Bowen On BEST ANSWER

As @r00tdev explained, using the SynchronizationContext() class is the solution to what I was trying to do. Below you will see an example of how I used it to get the results of the worker thread back to the main UI thread. Thanks for the answer @r00tdev!


Example:

private void SQLTest()
{
    //Set up a sync context back to this main UI thread
    m_sql.SyncContext = SynchronizationContext.Current;

    //Being called from main UI thread 9
    m_sql.SQLErrorMessage = "FIRST CHANGE";
    Task<SQLConnectionProperties> taskConnect = Task.Factory
        .StartNew(() => threadConnect(m_sql), CancellationToken.None, TaskCreationOptions.None, TaskScheduler.Default)
        .ContinueWith((task) => threadConnect(task.Result, true));
    //Continue back to main UI (still in main UI thread 9)
}

private SQLConnectionProperties threadConnect(SQLConnectionProperties sql, bool bContinuation = false)
{
    if (!bContinuation)
    {
        //In new thread 10
        sql.SQLErrorMessage = "SECOND CHANGE";
    }
    else
    {
        //In new thread 11
        sql.SQLErrorMessage = "THIRD CHANGE";

        sql.SyncContext.Post(threadConnectComplete, sql);
    }
    return sql;
}

private void threadConnectComplete(object state)
{
    //Back in the main UI thread!  Save the changes to the sql object
    SQLConnectionProperties sql = state as SQLConnectionProperties;
    m_sql = sql;

    //See the results and use this area to update the main UI
    Debug.WriteLine("SQLErrorMessage:" + sql.SQLErrorMessage);

    //TESTING NON THREAD SAFE just to show you no runtime errors happen
    form_MainUI.textbox1.Text = sql.SQLErrorMessage;
}
3
r00tdev On

you need to use _synchronizationContext.Send or _synchronizationContext.Post to communicate with UI thread.

Also, from what you have mentioned, I think you also need to pass "false" when calling either of above functions so that it knows that the function is not invoked from UI thread.

_synchronizationContext = SynchronizationContext.Current