#.net #task-parallel-library
#.net #задача-параллельная-библиотека
Вопрос:
Я работаю с TPL, и мне нужно, чтобы длительная задача TPL отправляла результаты в родительский поток пользовательского интерфейса без завершения. Я попробовал несколько подходов и довольно долго искал в Google. Кто-нибудь знает, как это сделать с помощью TPL?
Ответ №1:
Вы могли бы передать делегат для вызова с периодическими результатами, и SynchronizationContext
который задача могла бы использовать для вызова обратного вызова в правильном потоке. Это в основном способ, которым это BackgroundWorker
делается (и способ, которым асинхронная функция C # 5 будет «знать», куда вам перезвонить) — она захватывает SynchronizationContext.Current
вызывающий поток, затем вызывает Post
(IIRC) для отправки сообщения в нужный контекст. Затем вам просто нужно обернуть исходный обратный вызов в a SendOrPostCallback
, который выполняет его, когда он попадает в нужный поток.
РЕДАКТИРОВАТЬ: пример программы:
using System;
using System.Windows.Forms;
using System.Threading;
using System.Threading.Tasks;
class Test
{
static void Main()
{
Form form = new Form();
Label label = new Label();
form.Controls.Add(label);
form.Load = delegate { HandleLoaded(label); };
Application.Run(form);
}
static void HandleLoaded(Label label)
{
Action<string> callback = text => label.Text = text;
StartTask(callback);
}
static void StartTask(Action<string> callback)
{
SendOrPostCallback postCallback = obj => callback((string) obj);
SynchronizationContext context = SynchronizationContext.Current;
Task.Factory.StartNew(() => {
for (int i = 0; i < 100; i )
{
string text = i.ToString();
context.Post(postCallback, text);
Thread.Sleep(100);
}
});
}
}
Комментарии:
1. @Kaizen: Готово. Обратите внимание, что
StartTask
метод ничего не знает о том факте, что это приложение Windows Forms — тот же код будет работать в приложении WPF.2. Я столкнулся с небольшой проблемой, пытаясь заставить это работать с помощью модульного теста. Сначала текущий SynchronizationContext был нулевым, затем, когда я его создал, я все еще не смог выполнить обратный вызов в родительском потоке. Вместо этого обратный вызов был отправлен в новый поток. Знаете ли вы, как ответить родительскому потоку?
3. @Kaizen: это зависит от родительского потока. Я подозреваю, что в модульном тестировании вы уже работаете с потоком пула потоков, поэтому нет ничего похожего на перекачку событий пользовательского интерфейса.
Ответ №2:
В зависимости от используемого вами приложения могут быть разные подходы.
- WinForms: управление.Вызвать
- WPF / Silverlight / WP7 и производные: Диспетчер.Вызвать
- Какой-либо другой тип приложения: определите, что означает поток пользовательского интерфейса в первую очередь?