1

We recently adopted the TPL as the toolkit for running some heavy background tasks.

These tasks typically produce a single object that implements IDisposable. This is because it has some OS handles internally.

What I want to happen is that the object produced by the background thread will be properly disposed at all times, also when the handover coincides with application shutdown.

After some thinking, I wrote this:

    private void RunOnUiThread(Object data, Action<Object> action)
    {
        var t = Task.Factory.StartNew(action, data, CancellationToken.None, TaskCreationOptions.None, _uiThreadScheduler);
        t.ContinueWith(delegate(Task task)
            {
                if (!task.IsCompleted)
                {
                    DisposableObject.DisposeObject(task.AsyncState);
                }
            });            
    }

The background Task calls RunOnUiThread to pass its result to the UI thread. The task t is scheduled on the UI thread, and takes ownership of the data passed in. I was expecting that if t could not be executed because the ui thread's message pump was shut down, the continuation would run, and I could see that that the task had failed, and dispose the object myself. DisposeObject() is a helper that checks if the object is actually IDisposable, and non-null, prior to disposing it.

Sadly, it does not work. If I close the application after the background task t is created, the continuation is not executed.

I solved this problem before. At that time I was using the Threadpool and the WPF Dispatcher to post messages on the UI thread. It wasn't very pretty, but in the end it worked. I was hoping that the TPL was better at this scenario. It would even be better if I could somehow teach the TPL that it should Dispose all leftover AsyncState objects if they implement IDisposable.

So, the code is mainly to illustrate the problem. I want to learn about any solution that allows me to safely handover Disposable objects to the UI thread from background Tasks, and preferably one with as little code as possible.

4

3 回答 3

1

当一个进程关闭时,它的所有内核句柄都会自动关闭。你不应该担心这个:

http://msdn.microsoft.com/en-us/library/windows/desktop/ms686722(v=vs.85).aspx

于 2012-02-11T20:50:53.323 回答
0

看看 RX 库。这可以让你做你想做的事。

于 2012-02-10T22:43:20.397 回答
0

来自MSDN

IsCompleted当任务处于以下三种最终状态之一时将返回 true:RanToCompletion、、FaultedCanceled

换句话说,您DisposableObject.DisposeObject永远不会被调用,因为在上述条件之一发生后,将始终安排继续。我相信你的意思是:

t.ContinueWith(t => DisposableObject.DisposeObject(task.AsyncState),
               TaskContinuationOptions.NotOnRanToCompletion)

(顺便说一句,您可以简单地捕获data变量而不是使用AsyncState属性)

但是,对于您想要确保始终发生的事情,我不会使用延续。我相信一个try-finally块在这里会更合适:

private void RunOnUiThread2(Object data, Action<Object> action)
{
    var t = Task.Factory.StartNew(() => 
    {
        try
        {
            action(data);
        }
        finally
        {
            DisposableObject.DisposeObject(task.AsyncState); 
            //Or use a new *foreground* thread if the disposing is heavy
        }
    }, CancellationToken.None, TaskCreationOptions.None, _uiThreadScheduler);
}
于 2012-02-11T18:55:54.340 回答