2013-04-25 94 views
-1

我创建了一个使用Observable Lists的应用程序。我已经使ObservableList类线程安全(我认为),现在在我的应用程序中工作正常。Windows服务中的线程处理

现在我试图安装我的应用程序作为服务。这工作正常,直到某些东西被添加到列表中。我认为那里的线程死了。我有以下代码:

/// <summary> 
/// Creates a new empty ObservableList of the provided type. 
/// </summary> 
public ObservableList() 
{ 
    //Assign the current Dispatcher (owner of the collection) 
    _currentDispatcher = Dispatcher.CurrentDispatcher; 
} 

/// <summary> 
/// Executes this action in the right thread 
/// </summary> 
///<param name="action">The action which should be executed</param> 
private void DoDispatchedAction(Action action) 
{ 
    if (_currentDispatcher.CheckAccess()) 
     action.Invoke(); 
    else 
     _currentDispatcher.Invoke(DispatcherPriority.DataBind, action); 
} 

/// <summary> 
/// Handles the event when a collection has changed. 
/// </summary> 
/// <param name="e"></param> 
protected override void OnCollectionChanged(NotifyCollectionChangedEventArgs e) 
{ 
    DoDispatchedAction(() => base.OnCollectionChanged(e)); 
} 

调试时,我看到Collection.Add(object)被调用。它启动了DoDispatchedAction函数,调试器最后一个命令是_currentDispatcher.Invoke(DispatcherPriority.DataBind, action);。之后,应用程序将继续执行,但Collection.Add(object)之后的代码不会再执行。最初将项目添加到ObservableList的代码不会继续。这就是为什么我认为线程死亡或类似的东西。

当检查在调试器的动作,我发现,以下消息在那里:

的ApartmentState =“_currentDispatcher.Thread.ApartmentState”抛出 异常类型“System.Threading.ThreadStateException”的

我该如何解决这个问题?我是否正朝着正确的方向思考?

+0

你为什么在服务中使用Dispatcher? – ken2k 2013-04-29 11:42:07

+0

我不知道。我有几个线程正在运行并观察一些硬件组件。你有什么建议? – Joetjah 2013-04-29 11:48:36

+0

“OnCollectionChanged”事件附加了什么样的处理程序?由于您没有UI(Windows服务),我怀疑它不需要将您的事件处理程序编组到特定的线程。 – ken2k 2013-04-29 11:52:55

回答

1

由于这是一个与硬件相关的服务,这与通常的LOB风格的应用程序有点不同。区别在于:应该触发事件的更改来自应用程序的后端,而整个UI框架和服务体系结构则用于使前端要求后端提供的数据。

你可以通过在他们相遇的地方创建某种“中立地”来把这两者结合在一起。

在硬件处理组件中,我会有一个后台线程连续运行或由硬件中断触发,并使用从硬件收集的任何数据更新其数据结构。然后,我会有一个同步方法,可以在调用硬件数据时创建一致的硬件数据快照。

在WPF客户端中,将会有一个调度程序计时器,它将以设定的时间间隔调用此方法,并使用数据快照更新ObservableCollections。这是可能的,因为它会发生在UI线程上。实际上,如果可能的话,您应该尝试添加和删除ObservableCollections中的项目,而不是创建新的集合实例,除非集合中的数据从一个调用完全更改为下一个。

WCF客户端只会是创建数据快照的方法的一个包装:它只会在调用它时发回这样的快照。

WCF服务的WPF客户端将作为本地WPF客户端工作,只会直接调用服务而不是硬件库,并且可能会为DispatcherTimer选择更长的时间间隔,以避免过多网络流量。您可以通过返回一个特殊的代码来进一步优化它,这意味着“没有任何变化”,以避免多次发送相同的数据,或者使用单独的方法来询问数据是否已更改并检索已更改的数据。

+0

我喜欢这个想法。然后,“中立地”中的列表可能会被像RaiseProperty这样的事件填充,但我可以将ObservableList集合保留在WPF客户端中。 – Joetjah 2013-05-01 09:12:28

1

据我所知,你有一个应该作为Windows服务运行的核心代码和一个使用相同核心代码的WPF应用程序。

所以基本上你应该有类似解决方案中的3个项目:

  • 核心组件做一些与硬件相关的工作
  • 将被安装为Windows服务的可执行文件。此可执行文件引用的核心组件
  • 一个WPF应用程序,还引用了核心组件

调度员是有帮助的马歇尔回一个动作到UI线程。这基本上用于在WPF应用程序的UI线程中执行一些代码。例如,当您将一个集合绑定到DataGrid时,必须在UI线程上触发CollectionChanged事件,因为它会导致(由于绑定)更新UI。并且UI 必须从UI线程更新

由于没有UI更新,您的核心程序集不应该与调度程序打交道。您可以在这里使用简单的Collection,因为您不会将其绑定到任何UI组件。您的Windows服务可执行文件也一样。

另一方面,对于您的WPF应用程序,您可以使用绑定在UI组件上的ObservableCollection(例如DataGrid)。只有在这个程序集中,您必须确保UI组件始终从UI线程更新(这意味着您需要使用Dispatcher)。

所以,一个代码示例:

核心组件:

public IEnumerable<SomeClass> GetHardwareInfo() 
{ 
    return new List<SomeClass> { ... }; 
} 

Windows服务可执行文件:

internal static void Main(string[] args) 
{ 
    ... 
    var objs = new MyCoreInstance().GetHardwareInfo(); 
    ... 
} 

WPF应用程序(假设它的视图模型):

// Some UI component is binded to this collection that is obersvable 
public ObservableCollection<SomeClass> MyCol 
{ 
    get 
    { 
     return this.myCol; 
    } 

    set 
    { 
     if (this.myCol != value) 
     { 
      this.myCol = value; 
      this.RaisePropertyChanged("MyCol"); 
     } 
    } 
} 

public void UpdateList() 
{ 
    var info = new MyCoreInstance().GetHardwareInfo(); 

    // Now, marshall back to the UI thread to update the collection 
    Application.Current.Dispatcher.Invoke(() => 
     { 
      this.MyCol = new ObservableCollection(info); 
     }); 
} 
+0

听起来很合理。但是什么时候调用'UpdateList'方法呢?我使用ObservableList的原因是因为ListBox会在绑定到它的数据发生更改后自行更新。这仍然是可以实现的,虽然它不再是服务代码中的ObservableList? – Joetjah 2013-04-29 12:31:23

+0

@Joetjah在我提供的代码中,MyCol集合是一个ObservableCollection,所以你一定可以将它绑定到你的ListBox。您可以从WPF应用程序(后台工作线程或主UI线程)中的任何位置调用'UpdateList',这要归功于'Application.Current.Dispatcher.Invoke'。 – ken2k 2013-04-29 12:34:31

+0

硬件组件使用相同的ObservableCollection列表将更改值传递给核心逻辑(服务)。这是否意味着我必须将所有这些更改为正常的列表,并使用事件处理来代替,比如RaisePropertyChanged?这意味着只要您尝试开发服务,ObservableCollection就会变得毫无用处,不是吗? – Joetjah 2013-04-29 12:39:34