Як створити завдання (TPL) із запуском потоку STA?


75

Використання Thread досить просто

 Thread thread = new Thread(MethodWhichRequiresSTA);
 thread.SetApartmentState(ApartmentState.STA);  

Як виконати те саме за допомогою Завдань у програмі WPF? Ось деякий код:

Task.Factory.StartNew
  (
    () => 
    {return "some Text";}
  )
   .ContinueWith(r => AddControlsToGrid(r.Result));  

Я отримую InvalidOperationException з

Викликаючий потік повинен бути STA, оскільки багато компонентів інтерфейсу вимагають цього.

Відповіді:


75

Ви можете використовувати TaskScheduler.FromCurrentSynchronizationContext Метод, щоб отримати TaskScheduler для поточного контексту синхронізації (який є диспетчером WPF під час запуску програми WPF).

Потім використовуйте ContinueWith перевантаження, яке приймає TaskScheduler:

var scheduler = TaskScheduler.FromCurrentSynchronizationContext();

Task.Factory.StartNew(...)
            .ContinueWith(r => AddControlsToGrid(r.Result), scheduler);

10
Просто, щоб було зрозуміло; лише лямбда в методі ContinueWith буде працювати з належним контекстом, а не те, що виконується в основному лямбда-завданні.
dudeNumber4,

Дякую за це, я намагався TaskScheduler.Current(із обробника подій кнопки WinForms), не міг зрозуміти, чому це не працює ...
Бенджол

11
Для уточнення існує також перевантаження для методу екземпляра Task.Start (), який приймає TaskScheduler. Деталі запитання дають зрозуміти, що нас більше цікавить випадок продовження, але більш загальне питання про те, як запустити Завдання на потоці STA, не обмежується лише цим випадком. Спочатку я помилково припустив, що мені потрібно буде перейти від порожнього завдання "видумки", щоб виконати бажане завдання STA.
Джош Саттерфілд,

2
Тож чи не слід редагувати заголовок цього запитання, щоб краще відображати його намір?
Mrchief

Або розбитий на дві частини. "Як розпочати завдання у потоці STA" та "Як продовжити завдання у потоці STA".
Девід


0

Диспетчер. Виклик може бути рішенням. напр

    private async Task<bool> MyActionAsync()
    {
        // await for something, then return true or false
    }
    private void StaContinuation(Task<bool> t)
    {
        myCheckBox.IsChecked = t.Result;
    }
    private void MyCaller()
    {
        MyActionAsync().ContinueWith((t) => Dispatcher.Invoke(() => StaContinuation(t)));
    }

The ContinueWithта the Dispatcherвважаються олдскульними підходами після появи async / await. Я б уникав використовувати їх для нових розробок (загалом).
Теодор Зуліас,
Використовуючи наш веб-сайт, ви визнаєте, що прочитали та зрозуміли наші Політику щодо файлів cookie та Політику конфіденційності.
Licensed under cc by-sa 3.0 with attribution required.