Ответ 1
Да, вы можете явно вызвать BeginInvoke
в окне/Элементе управления, с которым вы хотите установить связь. В вашем случае это будет выглядеть так:
this.textBox.BeginInvoke(new Action(() =>
{
this.textBox.Text = "From child task.";
}));
У меня есть небольшое приложение winforms, которое выполняет длительный процесс в другом потоке с помощью задачи TPL. Во время этого продолжительного процесса я хотел бы обновить пользовательский интерфейс (индикатор выполнения или что-то еще). Есть ли способ сделать это без необходимости .ContinueWith()?
public partial class Form1 : Form
{
private Task _childTask;
public Form1()
{
InitializeComponent();
Task.Factory.StartNew(() =>
{
// Do some work
Thread.Sleep(1000);
// Update the UI
_childTask.Start();
// Do more work
Thread.Sleep(1000);
});
_childTask = new Task((antecedent) =>
{
Thread.Sleep(2000);
textBox1.Text = "From child task";
}, TaskScheduler.FromCurrentSynchronizationContext());
}
}
Выполняя этот код, я получаю вездесущее исключение:
Неверная операция поперечного потока: элемент управления 'textBox1' доступен из потока, отличного от потока, на котором он был создан.
Да, вы можете явно вызвать BeginInvoke
в окне/Элементе управления, с которым вы хотите установить связь. В вашем случае это будет выглядеть так:
this.textBox.BeginInvoke(new Action(() =>
{
this.textBox.Text = "From child task.";
}));
Вы передаете TaskScheduler
как state
(или antecedent
, как вы его назвали). Это не имеет большого смысла.
Я не уверен, что именно вы хотите сделать, но вам нужно указать TaskScheduler
, когда вы начинаете Task
, а не когда вы его создаете. Кроме того, кажется, что childTask
не обязательно должно быть полем:
var scheduler = TaskScheduler.FromCurrentSynchronizationContext();
Task childTask = null;
Task.Factory.StartNew(
() =>
{
Thread.Sleep(1000);
childTask.Start(scheduler);
Thread.Sleep(1000);
});
childTask = new Task(
() =>
{
Thread.Sleep(2000);
textBox1.Text = "From child task";
});
Конечно, все это будет намного проще с С# 5 и await
:
public Form1()
{
InitializeComponent();
StartAsync();
}
private async void StartAsync()
{
// do some work
await Task.Run(() => { Thread.Sleep(1000); });
// start more work
var moreWork = Task.Run(() => { Thread.Sleep(1000); });
// update the UI, based on data from "some work"
textBox1.Text = "From async method";
// wait until "more work" finishes
await moreWork;
}
Вы не можете создать конструктор async
, но вы можете запустить из него метод async
. "Выполнение работы" не запускается в потоке пользовательского интерфейса, потому что он был запущен явно через Task.Run()
. Но так как StartAsync()
вызывался напрямую, он выполнялся в потоке пользовательского интерфейса.