WPF API可以安全地用在WCF服务中吗?

Sim*_*nro 13 c# wpf wcf xaml image-processing

我需要使用客户端XAML(来自Silverlight)并创建与服务器端资源(高分辨率图像)合并的位图,并且可以使用WPF(DrawingContext等)轻松地完成此操作.有人提到服务器端(在IIS WCF中托管)使用WPF类似于在服务器上运行Office,这是一个非常糟糕的主意.

WPF是否可以在服务器上运行?有哪些替代方案(特别是xaml)?我需要注意什么(内存泄漏,线程等)?

Ray*_*rns 7

在WCF后面使用WPF服务器端并不等同于运行Office服务器端!WPF作为一个整体只是一些DLL,并且与使用任何其他库服务器端没有什么不同.这与Word或Excel 完全不同,后者在后台加载整个应用程序,包括用户界面,加载项,脚本语言等.

我多年来一直在WCF背后的服务器上使用WPF.这是一个非常优雅和有效的解决方案:

  • 使用DirectX软件渲染是因为您没有使用实际的显示设备,但DirectX中的软件渲染例程已经过高度优化,因此您的性能和资源消耗将与您可能找到的任何渲染解决方案一样好,并且可能很多更好.

  • WPF的表现力允许使用优化的DirectX代码创建复杂的图形,而不是手工完成.

实际上,在WCF服务中使用WPF会为RAM占用空间增加大约10MB.

运行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;
  }
}
Run Code Online (Sandbox Code Playgroud)