API WPF может безопасно использоваться в сервисе WCF?

Я имею требование, чтобы взять сторону клиента XAML (от Silverlight) и создать битовый массив, объединенный с серверным ресурсом (изображение с высоким разрешением), и могу сделать это довольно легко использующее WPF (DrawingContext и т.д.). Было упомянуто, что сторона сервера (размещенный в IIS WCF) использование WPF сродни рабочему Office на сервере и действительно плохой идее.

WPF создается для работы сервера? Каковы альтернативы (особенно с xaml)? Что я должен высматривать (утечки памяти, распараллеливая и т.д.)?

13
задан McGarnagle 5 February 2013 в 22:38
поделиться

1 ответ

Использование WPF на стороне сервера за WCF не эквивалентно использованию Office на стороне сервера! WPF в целом - это всего лишь несколько DLL, и на самом деле ничем не отличается от использования любой другой библиотеки на стороне сервера. Это совершенно отличается от Word или Excel, где вы загружаете целое приложение за кулисами, включая пользовательский интерфейс, надстройки, язык сценариев и т. д.

Я использую WPF на сервере за WCF уже много лет. Это очень элегантное и эффективное решение:

  • Используется программный рендеринг DirectX, поскольку вы не рисуете на реальном устройстве отображения, но процедуры программного рендеринга в DirectX были высоко оптимизированы, поэтому производительность и потребление ресурсов будут не хуже, чем у любого решения для рендеринга, которое вы можете найти, а возможно, и намного лучше.

  • Выразительность WPF позволяет создавать сложную графику с помощью оптимизированного кода DirectX, а не делать это вручную.

Практически говоря, использование WPF внутри службы WCF добавит около 10 МБ к объему оперативной памяти.

У меня не было никаких проблем с утечкой памяти при использовании WPF на стороне сервера. Я также использую XamlReader для разбора XAML в деревья объектов и обнаружил, что когда я перестаю ссылаться на дерево объектов, сборщик мусора собирает его без проблем. Я всегда полагал, что если бы я столкнулся с утечкой памяти в WPF, я бы обошел ее, запустив отдельный AppDomain, который вы будете время от времени перерабатывать, но на самом деле я никогда с этим не сталкивался.

Одна проблема с потоками, с которой вы столкнетесь, заключается в том, что WPF требует потоков STA, а WCF использует потоки MTA. Это не является существенной проблемой, поскольку вы можете иметь пул потоков STA для получения той же производительности, что и от потоков MTA. Я написал небольшой класс STAThreadPool, который обрабатывает переход. Вот он:

// A simple thread pool implementation that provides STA threads instead of the MTA threads provided by the built-in thread pool
public class STAThreadPool
{
  int _maxThreads;
  int _startedThreads;
  int _idleThreads;
  Queue<Action> _workQueue = new Queue<Action>();

  public STAThreadPool(int maxThreads)
  {
    _maxThreads = maxThreads;
  }

  void Run()
  {
    while(true)
      try
      {
        Action action;
        lock(_workQueue)
        {
          _idleThreads++;
          while(_workQueue.Count==0)
            Monitor.Wait(_workQueue);
          action = _workQueue.Dequeue();
          _idleThreads++;
        }
        action();
      }
      catch(Exception ex)
      {
        System.Diagnostics.Trace.Write("STAThreadPool thread threw exception " + ex);
      }
  }

  public void QueueWork(Action action)
  {
    lock(_workQueue)
    {
      if(_startedThreads < _maxThreads && _idleThreads <= _workQueue.Count)
        new Thread(Run) { ApartmentState = ApartmentState.STA, IsBackground = true, Name = "STAThreadPool#" + ++_startedThreads }.Start();
      _workQueue.Enqueue(action);
      Monitor.PulseAll(_workQueue);
    }
  }

  public void InvokeOnPoolThread(Action action)
  {
    Exception exception = null;
    using(ManualResetEvent doneEvent = new ManualResetEvent(false))  // someday:  Recycle these events
    {
      QueueWork(delegate
      {
        try { action(); } catch(Exception ex) { exception = ex; }
        doneEvent.Set();
      });
      doneEvent.WaitOne();
    }
    if(exception!=null)
      throw exception;
  }

  public T InvokeOnPoolThread<T>(Func<T> func)
  {
    T result = default(T);
    InvokeOnPoolThread(delegate
    {
      result = func();
    });
    return result;
  }
}
7
ответ дан 2 December 2019 в 01:20
поделиться
Другие вопросы по тегам:

Похожие вопросы: