2010-03-02 33 views
13

Tôi có một yêu cầu để có XAML phía khách hàng (từ Silverlight) và tạo một bitmap sáp nhập với một tài nguyên phía máy chủ (hình ảnh độ phân giải cao) và có thể làm điều này khá dễ dàng bằng cách sử dụng WPF (DrawingContext vv). Nó đã được đề cập rằng phía máy chủ (lưu trữ trong IIS WCF) sử dụng WPF là giống như chạy Office trên máy chủ và một ý tưởng thực sự xấu.API WPF có thể được sử dụng an toàn trong dịch vụ WCF không?

WPF có được xây dựng để chạy trên máy chủ không? Các lựa chọn thay thế (đặc biệt là với xaml) là gì? Tôi cần phải tìm những gì (rò rỉ bộ nhớ, luồng vv)?

+3

Tối đa 4 trong số đó có. –

+1

Câu hỏi này là có thật và không nên bị đóng như chủ quan và tranh luận. –

Trả lời

7

Sử dụng phía máy chủ WPF phía sau WCF là không tương đương với chạy Office server-side! WPF như một toàn thể chỉ là một vài DLL, và thực sự là không có gì khác hơn là sử dụng bất kỳ thư viện phía máy chủ khác. Đây là hoàn toàn khác với Word hoặc Excel, nơi bạn đang tải toàn bộ ứng dụng sau hậu trường, bao gồm giao diện người dùng, bổ trợ, ngôn ngữ kịch bản, v.v.

Tôi đã sử dụng WPF trên máy chủ phía sau WCF cho năm. Nó là một giải pháp rất thanh lịch và hiệu quả:

  • DirectX phần mềm dựng hình được sử dụng bởi vì bạn không vẽ trên một thiết bị hiển thị thực tế, nhưng các phần mềm dựng hình thói quen trong DirectX đã được tối ưu hóa cao do đó hiệu suất và tài nguyên của bạn tiêu thụ là sẽ tốt như bất kỳ giải pháp kết xuất nào bạn có thể tìm thấy và có thể tốt hơn nhiều.

  • Tính biểu thị của WPF cho phép tạo đồ họa phức tạp bằng cách sử dụng mã DirectX được tối ưu hóa thay vì thực hiện nó bằng tay.

Thực tế, việc sử dụng WPF từ dịch vụ WCF sẽ tăng khoảng 10MB vào dung lượng RAM của bạn.

Tôi chưa gặp phải bất kỳ sự cố rò rỉ bộ nhớ nào khi chạy phía máy chủ WPF. Tôi cũng sử dụng XamlReader để phân tích XAML thành cây đối tượng và đã thấy rằng khi tôi ngừng tham chiếu cây đối tượng, bộ thu gom rác thu thập nó mà không có vấn đề gì. Tôi luôn luôn nghĩ rằng nếu tôi đã chạy vào một rò rỉ bộ nhớ trong WPF tôi sẽ làm việc xung quanh nó bằng cách chạy trong một AppDomain riêng biệt mà bạn thỉnh thoảng sẽ tái chế, nhưng tôi không bao giờ thực sự gặp phải một.

Một vấn đề về luồng mà bạn gặp phải là WPF yêu cầu các chủ đề STA và WCF sử dụng các luồng MTA. Đây không phải là một vấn đề đáng kể vì bạn có thể có một nhóm các chủ đề STA để có được hiệu năng giống như bạn làm từ các luồng MTA. Tôi đã viết một lớp STAThreadPool ít xử lý việc chuyển đổi. Dưới đây là:

// 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

Nếu ứng dụng WPF đang chạy trên máy chủ có cạc đồ họa cao cấp hoặc tốt hơn, một vài trong số đó, có cách nào để buộc nó hiển thị bằng thẻ cụ thể không? –

+0

Tôi không biết cách nào để buộc tải các tính toán hình ảnh lên GPU.Nó xảy ra tự động khi ứng dụng máy chủ đang chạy trên một máy tính để bàn thực sự được kết nối với một màn hình vật lý. Nó cũng có thể xảy ra tự động vào những lúc khác. –

+0

@RayBurns - Tôi đang cố gắng sử dụng WPF để vẽ một jpeg trong Trình xử lý Http, giống như một loại hình mờ. Tôi nhận được lỗi STA, nhưng tôi không hoàn toàn chắc chắn làm thế nào để sử dụng giải pháp của bạn để sửa chữa nó. Nếu tôi thực hiện công việc của mình trên một luồng riêng biệt, trình xử lý Http của tôi sẽ không trả về trước khi nó kết thúc? – xr280xr

3

Mở rộng những gì rayburns đã nói ở đây là cách tôi sử dụng STAthread, WPF và Asp.net WebApi. Tôi đã sử dụng các tiện ích mở rộng song song, cụ thể là tệp này bên dưới.

//-------------------------------------------------------------------------- 
// 
// 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; 
      } 
     } 
    } 
} 

Cách sử dụng khá dễ dàng. Chỉ cần sử dụng mã bên dưới để sử dụng tiện ích mở rộng

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; 
Các vấn đề liên quan