使用 COM 对象的后台工作线程会导致 UI 线程挂起



我有一个专用的类,其中包括一个BackgroundWorkers,负责从队列中运行特定于类的操作 - 需要使用COM对象的操作。

专用类的对象是在运行时应用程序启动时从 UI 线程 (WPF( 创建的。调用类的构造函数时,它会实例化一个 BackgroundWorker ,该 Worker 异步运行从 UI 线程分配的操作。

但是,当这些操作需要 COM 对象生成的数据时,我注意到 UI 线程正在等待后台工作线程完成操作,然后再对用户输入做出反应。

如何隔离,以便 UI 线程不受最多可能需要 10 秒才能完成的 COM 函数的影响?

法典:

public class User(){
private BackgroundWorker Worker;
private Queue<Action> ActionQueue;
private COM COMObject; // COM is an interface exposed by the COM referenced in VS project
private bool Registered;
public User(){
this.Registered = true;
this.ActionQueue = new Queue<Action>();
this.Worker = new BackgroundWorker();
this.Worker.DoWork += new DoWorkEventHandler(DoWork);
this.Worker.DoWork += new RunWorkerCompletedEventHandler(WorkerCompleted);
this.Worker.Worker.WorkerSupportsCancellation = true;
this.Worker.Worker.RunWorkerAsync();
this.COMObject = new COM();
}
private DoWork(object sender, DoWorkEventArgs e){
// If there is something to be done (an action) in the queue
if (ActionQueue.Count > 0){
// Dequeue the action from the queue
Action queuedAction = ActionQueue.Dequeue();
// Do the action
queuedAction();
}
}
private void WorkerCompleted(object sender, RunWorkerCompletedEventArgs e){
// While this machine continues to be registered to the app...
if (this.Registered)
{
Worker.RunWorkerAsync();
}
}
public void ConnectToDatabase(){
Action action = delegate {
COMObject.Connect(); // function can take up to 10 seconds to return 

}; // end of action delegate
ActionQueue.Enqueue(action);
}
}

使用代码(在 UI 线程中(:

User user = new User();
user.ConnectToDatabase();

在我的 UI 中,在应用程序启动期间,最多可以创建 10 个User对象并调用以进行连接。如果我注释掉 User::ConnectToDatabase 中的COMObject.Connect();行并替换为Thread.Sleep(10000)则 UI 线程不会等待 10+ 秒。但是,就像现在的代码一样,我注意到在再次处理 WPF 应用程序中的任何用户输入之前,COMObject.Connect();行确实会导致 10+ 秒。

如何隔离,以便与 COM 对象相关的函数不会影响 UI 线程的性能?

(注意:与后台工作线程排队的操作没有与 UI 线程的交互。在这些操作中仅更改特定于类的属性(。

答案总是潜伏在评论中:)

正如 @Blindy 和 @jdweng 指出的那样,new COM()是在主 UI 线程上调用的,而 COM 对象的所有功能都在不同的线程上使用。

此外,我确实使用 STAThread 属性 (this.Worker.SetApartmentState(ApartmentState.STA);( 设置了 COM 对象的线程。

而且,我确实从使用BackgroundWorker 更改为实际的Thread。

最后但并非最不重要的一点是,当@Blindy指出使用Queue<Action>在从主 UI 线程排队的工作线程上工作的问题时,我最终确实使用了ConcurrentQueue<Action>,根据 @Anders H 的建议。我会使用 Tasks,从我对这个主题所做的大量研究来看,这将解决跨线程访问的潜在问题,但是,由于排队的"工作"必须按顺序完成并与 COM 对象相关,ConcurrentQueue 最终看起来是一个不错的解决方案暂时。但是,以后将不得不重新审视这一点。

最新更新