В представлении-модели я использую завод:Асинхронных-Await, кажется, использует поток пользовательского интерфейса
private async Task<BaseData> InitializeAsync()
{
await InstancesAsync();
await ProjectsAsync();
await AdminAsync();
return this;
}
public static async Task<BaseData> CreateAsync()
{
var ret = new BaseData();
return await ret.InitializeAsync();
}
Долгожданные методы довольно staightforward, например, с
var instances = await TaskEx.Run(new Func<List<string>>(() => Agent.GetInstances()));
По мнению МОФ Я хочу, чтобы установить DataContext в конструкторе:
Loaded += delegate
{
Dispatcher.Invoke(new Action(async() => { DataContext = await BasisGegevens.CreateAsync(); }));
};
Хотя это работает, я чувствую себя довольно неудобно, потому что поток пользовательского интерфейса используется везде, а также после того, как обратные вызовы когда ожидание завершено. Что мне не хватает?
Также я не понимаю, как использовать фабричный шаблон для DataContext
, потому что без Invoke
выше я получаю сообщение об ошибке, что другой объект принадлежит этому объекту.
EDIT: используя идеи г-Клири я получаю:
Loaded += async (object sender, RoutedEventArgs e) =>
{ DataContext = await BaseData.CreateAsync(); };
public static Task<BaseData> CreateAsync()
{
var ret = new BaseData();
return ret.InitializeAsync();
}
private async Task<BaseData> InitializeAsync()
{
// UI thread here
await InstancesAsync().ConfigureAwait(false);
// thread 'a' here
await ProjectsAsync().ConfigureAwait(false);
// thread 'a' sometimes 'b' here
await AdminAsync().ConfigureAwait(false);
// thread 'a' or 'b' here
return this;
}
Это работает отлично, за исключением того, что я не могу понять, как ConfigureAwait(false)
работы.
Внутри метода InstancesAsync()
я имею ждали задания:
var instances = await TaskEx.Run(new Func<List<string>>(() => Agent.GetInstances()));
После ожидания repsonse, я возвращаюсь в потоке пользовательского интерфейса - Я никогда не ожидал, что произойдет!
Обратите внимание, что ProjectsAsync()
и AdminAsync()
ведут себя одинаково, хотя они начинаются с рабочего (или заднего) потока!
Я понял, что ConfigureAwait(true)
имеет эффект возврата в вызывающем потоке (в моем случае поток пользовательского интерфейса). Я тестировал это, и это так.
Почему я тоже вижу это с ConfigureAwait(false)
: из-за вложенного ожидания см. Комментарии.
Что является источником события «Загружено» в коде, который вы показали? Если это регулярное событие ['FrameworkElement.Loaded'] (http://msdn.microsoft.com/en-us/library/system.windows.frameworkelement.loaded (v = vs.110) .aspx), оно должно быть запущен в потоке пользовательского интерфейса. Вы создаете представление для отдельного потока? Возможно, это потому, что вы вызываете «Агент».GetInstances() 'в потоке пула non-UI? – Noseratio
Событие Loaded является обычным событием FrameworkElement.Loaded. Я намеревался создать представление в потоке пользовательского интерфейса, которое оказалось ошибочным. Agent.GetInstances() запускается в ожидаемой задаче, я ожидал, что это будет поток потока, отличного от UI, но это не так. – Gerard
'TaskEx.Run' использует отдельный поток пулов для запуска вашего делегата' Func', в котором выполняется 'Agent.GetInstances()'. Я бы посоветовал вам следовать за ответом Стивена и убедиться, что оба элемента интерфейса WPF и объекты ViewModel созданы на одном уровне - UI-thread. Добавьте несколько журналов ('System.Threading.Thread.CurrentThread.ManagedThreadId'), чтобы узнать, в каком потоке вы находитесь и где. – Noseratio