Вы можете включить поток управления на его голову, используя DispatcherFrames, что позволяет десериализации выполняться в потоке пользовательского интерфейса в фоновом режиме.
Для начала вам нужен способ периодически получать контроль во время десериализации. Независимо от того, какой десериализатор вы используете, ему придется вызывать наборы свойств для ваших объектов, поэтому вы обычно можете добавлять код к установщикам свойств. В качестве альтернативы вы можете изменить десериализатор. В любом случае, убедитесь, что ваш код вызывается достаточно часто
Каждый раз, когда вы получаете контроль, все, что вам нужно сделать, это:
- Создать DispatcherFrame
- Поставить в очередь событие диспетчеру, используя BeginInvoke, который устанавливает Continue = false в кадре
- Используйте PushFrame для запуска кадра, работающего на Диспетчере
Кроме того, при вызове самого десериализатора убедитесь, что вы делаете это из Dispatcher.BeginInvoke, или что ваш код вызова не удерживает никаких блокировок и т. Д.
Вот как это будет выглядеть:
public partial class MyWindow
{
SomeDeserializer _deserializer = new SomeDeserializer();
byte[] _sourceData;
object _deserializedObject;
...
void LoadButton_Click(...)
{
Dispatcher.BeginInvoke(DispatcherPriority.Background, new Action(() =>
{
_deserializedObject = _deserializer.DeserializeObject(_sourceData);
}));
}
}
public class OneOfTheObjectsBeingDeserializedFrequently
{
...
public string SomePropertyThatIsFrequentlySet
{
get { ... }
set { ...; BackgroundThreadingSolution.DoEvents(); }
}
}
public class BackgroundThreadingSolution
{
[ThreadLocal]
static DateTime _nextDispatchTime;
public static void DoEvents()
{
// Limit dispatcher queue running to once every 200ms
var now = DateTime.Now;
if(now < _nextDispatchTime) return;
_nextDispatchTime = now.AddMilliseconds(200);
// Run the dispatcher for everything over background priority
var frame = new DispatcherFrame();
Dispatcher.CurrentDispatcher.BeginInvoke(DispatcherPriority.Background, new Action(() =>
{
frame.Continue = false;
}));
Dispatcher.PushFrame(frame);
}
}
Проверка DateTime.Now в DoEvents () фактически не требуется для работы этого метода, но улучшит производительность, если SomeProperty будет установлен очень часто во время десериализации.
Редактировать: Сразу после того, как я написал это, я понял, что есть более простой способ реализовать метод DoEvents. Вместо использования DispatcherFrame просто используйте Dispatcher.Invoke с пустым действием:
public static void DoEvents()
{
// Limit dispatcher queue running to once every 200ms
var now = DateTime.Now;
if(now < _nextDispatchTime) return;
_nextDispatchTime = now.AddMilliseconds(200);
// Run the dispatcher for everything over background priority
Dispatcher.CurrentDispatcher.Invoke(DispatcherPriority.Background, new Action(() => {}));
}