Безопасная передача объекта `Disposable` в поток пользовательского интерфейса с помощью TPL - PullRequest
1 голос
/ 11 февраля 2012

Недавно мы приняли TPL в качестве инструментария для выполнения некоторых сложных фоновых задач.

Эти задачи обычно создают один объект, который реализует IDisposable.Это потому, что у него есть некоторые операционные системы внутри.

Я хочу, чтобы объект, созданный фоновым потоком, всегда был правильно расположен, даже когда передача обслуживания совпадает с завершением работы приложения.

Подумав, я написал:

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

Фон Task вызывает RunOnUiThread для передачи своего результата в поток пользовательского интерфейса.Задача t запланирована в потоке пользовательского интерфейса и становится владельцем переданного data. Я ожидал, что если t не удастся выполнить из-за того, что насос сообщений потока пользовательского интерфейса был отключен, продолжение продолжится,и я мог видеть, что задание не выполнено, и сам распоряжался объектом.DisposeObject() - это помощник, который проверяет, является ли объект действительно идентифицируемым и ненулевым, перед его удалением.

К сожалению, это не работает.Если я закрою приложение после создания фоновой задачи t, продолжение не будет выполнено.

Я решил эту проблему раньше.В то время я использовал Threadpool и диспетчер WPF для публикации сообщений в потоке пользовательского интерфейса.Это было не очень красиво, но в конце концов это сработало.Я надеялся, что TPL был лучше в этом сценарии.Было бы даже лучше, если бы я мог как-то научить TPL, что он должен утилизировать все оставшиеся объекты AsyncState, если они реализуют IDisposable.

Итак, код предназначен в основном для иллюстрации проблемы.Я хочу узнать о любом решении, которое позволяет мне безопасно передавать одноразовые объекты потоку пользовательского интерфейса из фоновых задач, и желательно с минимальным количеством кода.

Ответы [ 3 ]

1 голос
/ 12 февраля 2012

Когда процесс закрывается, все его дескрипторы ядра автоматически закрываются. Вам не нужно беспокоиться об этом:

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

0 голосов
/ 11 февраля 2012

С MSDN :

IsCompleted вернет true, когда Задача находится в одном из трех конечные состояния: RanToCompletion, Faulted или Canceled

Другими словами, ваш 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);
}
0 голосов
/ 11 февраля 2012

Посмотрите библиотеку RX. Это может позволить вам делать то, что вы хотите.

...