Daniel Crowe
Daniel Crowe

Reputation: 321

Continuing code on another thread

I'm writing some code that is invoked on the UI thread, invokes some code on another thread (not the ThreadPool, but the same thread each time), and then resumes on the UI thread. I'd like some advice on the best async way to do this.

The complexity of the EnsureThread method is because the other thread must be the same thread every time and must be STA with a Dispatcher running on it. This is because I need to use MCI, but do not want it running on the UI thread. See here https://stackoverflow.com/a/32711239/420159.

I create the second thread like so:

private static void EnsureThread()
{
    if (eventLoopThread != null)
    {
        return;
    }

    lock (eventLoopLock)
    {
        if (eventLoopThread == null)
        {
            var lck = new EventWaitHandle(false, EventResetMode.ManualReset);

            var t = new Thread(() =>
            {
                try
                {
                    // create dispatcher and sync context
                    var d = Dispatcher.CurrentDispatcher;
                    var context = new DispatcherSynchronizationContext(d);
                    SynchronizationContext.SetSynchronizationContext(context);

                    // create taskfactory
                    eventLoopFactory = new TaskFactory(TaskScheduler.FromCurrentSynchronizationContext());
                    eventLoopDispatcher = d;
                }
                finally
                {
                    lck.Set();
                }

                // run the event loop
                Dispatcher.Run();
            });
            t.SetApartmentState(ApartmentState.STA);
            t.IsBackground = true;
            t.Start();

            lck.WaitOne();
            lck.Dispose();

            eventLoopThread = t;
        }
    }
}

and then I call the second thread like so:

async void button_click(...)
{
    // do something 1

    await eventLoopFactory.StartNew(()=>
    {
        // do something 2
    });

    // do something 3
}

Is there a better way to do it?

Upvotes: 2

Views: 133

Answers (2)

Mike Eason
Mike Eason

Reputation: 9723

Your Button_Click already seems to be doing way too many things, you should first split this method into separate methods.

async void Button_Click(...)
{
    await DoSomething1();
    await DoSomething2();
    await DoSomething3();
}

Now, your other methods will look something like this:

async Task DoSomething1()
{
    await Task.Run(() =>
    {
        ...
    });
}

This will allow your Button_Click method to asynchronously perform these tasks (in order) and keep your UI responsive.

Upvotes: 1

Fabio Salvalai
Fabio Salvalai

Reputation: 2509

You don't really need to use a thread factory if you want to run a delegate on a thread of the ThreadPool

just use

        await Task.Run(() =>
        {
            // do something 2
        });

That way, you don't need to run your // do something code on an event loop thread, but it will be run on an available thread from the thread pool.

You shouldn't create a second thread yourself. Thread pools are the right tool for that, as it will recycle idle threads in a very efficient way.

Upvotes: 4

Related Questions