2010-03-02 87 views
13

我有一个需要采取客户端XAML(从Silverlight),并创建一个位图与服务器端资源合并(高分辨率图像),并可以很容易地使用WPF(DrawingContext等)来做到这一点。已经提到服务器端(在IIS WCF中托管)使用WPF类似于在服务器上运行Office,这是一个非常糟糕的主意。WPF API可以在WCF服务中安全使用吗?

是否构建WPF以在服务器上运行?有什么选择(特别是XAML)?我需要注意什么(内存泄漏,线程等)?

+3

最多可以有4个。 – 2010-03-02 13:37:16

+1

这个问题是真实的,不应该因为主观和议论而关闭。 – 2010-03-02 13:37:58

回答

7

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

我一直在WCF服务器后面使用WPF for年份。这是一个非常优雅和高效的解决方案:

  • 的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; 
    } 
} 
+0

如果WPF应用程序在高端图形卡的服务器上运行,或者更好,那么其中的一些有什么方法可以强制它使用特定的卡进行渲染? – 2010-03-04 21:50:26

+0

我不知道有什么方法强制将成像计算卸载到GPU上。它在服务器应用程序在实际连接到物理显示器的桌面上运行时自动发生。它也可能在其他时间自动发生。 – 2010-03-05 07:15:03

+0

@RayBurns - 我试图用WPF在Http Handler中绘制一个jpeg,有点像水印类型的东西。我收到STA错误,但我不太清楚如何使用您的解决方案来修复它。如果我在单独的线程上完成我的工作,那么在它完成之前,我的Http处理程序不会返回吗? – xr280xr 2012-09-17 16:14:44

3

扩展什么rayburns说这里是我如何使用STAthread,WPF和Asp.net WebApi。我使用了并行的扩展,特别是下面的这个文件。

//-------------------------------------------------------------------------- 
// 
// Copyright (c) Microsoft Corporation. All rights reserved. 
// 
// File: StaTaskScheduler.cs 
// 
//-------------------------------------------------------------------------- 

using System.Collections.Concurrent; 
using System.Collections.Generic; 
using System.Linq; 

namespace System.Threading.Tasks.Schedulers 
{ 
    public static class ParallelExtensions 
    { 
     public static Task StartNew(this TaskFactory factory, Action action, TaskScheduler scheduler) 
     { 
      return factory.StartNew(action, CancellationToken.None, TaskCreationOptions.None, scheduler); 
     } 

     public static Task<TResult> StartNew<TResult>(this TaskFactory factory, Func<TResult> action, TaskScheduler scheduler) 
     { 
      return factory.StartNew<TResult>(action, CancellationToken.None, TaskCreationOptions.None, scheduler); 
     } 

     public static Task<TResult> StartNewSta<TResult>(this TaskFactory factory, Func<TResult> action) 
     { 
      return factory.StartNew<TResult>(action, sharedScheduler); 
     } 

     private static TaskScheduler sharedScheduler = new StaTaskScheduler(1); 
    } 

    /// <summary>Provides a scheduler that uses STA threads.</summary> 
    public sealed class StaTaskScheduler : TaskScheduler, IDisposable 
    { 
     /// <summary>Stores the queued tasks to be executed by our pool of STA threads.</summary> 
     private BlockingCollection<Task> _tasks; 
     /// <summary>The STA threads used by the scheduler.</summary> 
     private readonly List<Thread> _threads; 

     /// <summary>Initializes a new instance of the StaTaskScheduler class with the specified concurrency level.</summary> 
     /// <param name="numberOfThreads">The number of threads that should be created and used by this scheduler.</param> 
     public StaTaskScheduler(int numberOfThreads) 
     { 
      // Validate arguments 
      if (numberOfThreads < 1) throw new ArgumentOutOfRangeException("concurrencyLevel"); 

      // Initialize the tasks collection 
      _tasks = new BlockingCollection<Task>(); 

      // Create the threads to be used by this scheduler 
      _threads = Enumerable.Range(0, numberOfThreads).Select(i => 
      { 
       var thread = new Thread(() => 
       { 
        // Continually get the next task and try to execute it. 
        // This will continue until the scheduler is disposed and no more tasks remain. 
        foreach (var t in _tasks.GetConsumingEnumerable()) 
        { 
         TryExecuteTask(t); 
        } 
       }); 
       thread.IsBackground = true; 
       thread.SetApartmentState(ApartmentState.STA); 
       return thread; 
      }).ToList(); 

      // Start all of the threads 
      _threads.ForEach(t => t.Start()); 
     } 

     /// <summary>Queues a Task to be executed by this scheduler.</summary> 
     /// <param name="task">The task to be executed.</param> 
     protected override void QueueTask(Task task) 
     { 
      // Push it into the blocking collection of tasks 
      _tasks.Add(task); 
     } 

     /// <summary>Provides a list of the scheduled tasks for the debugger to consume.</summary> 
     /// <returns>An enumerable of all tasks currently scheduled.</returns> 
     protected override IEnumerable<Task> GetScheduledTasks() 
     { 
      // Serialize the contents of the blocking collection of tasks for the debugger 
      return _tasks.ToArray(); 
     } 

     /// <summary>Determines whether a Task may be inlined.</summary> 
     /// <param name="task">The task to be executed.</param> 
     /// <param name="taskWasPreviouslyQueued">Whether the task was previously queued.</param> 
     /// <returns>true if the task was successfully inlined; otherwise, false.</returns> 
     protected override bool TryExecuteTaskInline(Task task, bool taskWasPreviouslyQueued) 
     { 
      // Try to inline if the current thread is STA 

      return 
       Thread.CurrentThread.GetApartmentState() == ApartmentState.STA && 
       TryExecuteTask(task); 
     } 

     /// <summary>Gets the maximum concurrency level supported by this scheduler.</summary> 
     public override int MaximumConcurrencyLevel 
     { 
      get { return _threads.Count; } 
     } 

     /// <summary> 
     /// Cleans up the scheduler by indicating that no more tasks will be queued. 
     /// This method blocks until all threads successfully shutdown. 
     /// </summary> 
     public void Dispose() 
     { 
      if (_tasks != null) 
      { 
       // Indicate that no new tasks will be coming in 
       _tasks.CompleteAdding(); 

       // Wait for all threads to finish processing tasks 
       foreach (var thread in _threads) thread.Join(); 

       // Cleanup 
       _tasks.Dispose(); 
       _tasks = null; 
      } 
     } 
    } 
} 

用法很简单。只需使用以下代码即可使用扩展名

Task<MemoryStream> Task1 = Task.Factory.StartNewSta(() => 
      { 

       /* use wpf here*/ 

       BitmapEncoder PngEncoder = 
        new PngBitmapEncoder(); 
       PngEncoder.Frames.Add(BitmapFrame.Create(Render)); 

       //save to memory stream 
       var Ms = new MemoryStream(); 

       PngEncoder.Save(Ms);     
       return Ms; 
      }); 
    Task.WaitAll(Task1); 

    return Task1.Result; 
相关问题