BackgroundWorker 使用 COM 对象导致 UI 线程挂起

BackgroundWorker Using a COM Object Results in UI Thread Hangup

我有一个专用的 class,其中包括一个 BackgroundWorker,负责 运行 class 来自队列的特定操作 - 需要使用 COM 对象的操作。

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

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

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

代码:

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 秒以上。但是,就像现在的代码一样,我注意到 COMObject.Connect(); 行确实在 10 秒后再次处理 WPF 应用程序中的任何用户输入。

如何进行隔离以使与 COM 对象相关的函数不影响 UI 线程的性能?

(注意:使用 BackgroundWorker 排队的操作与 UI 线程没有交互。在这些操作中仅更改了 class 特定的属性。

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

正如 @Blindy and @jdweng 指出的那样,new COM() 在主 UI 线程上被调用,而 COM 对象的所有功能都在另一个线程上使用。

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

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

最后但并非最不重要的一点是,正如 @Blindy called out the issue with using a Queue<Action> to do work on the Worker thread, queued from the main UI thread, I did end up using a ConcurrentQueue<Action>, per @Anders H 的建议。我会使用任务,根据我对该主题所做的大量研究,它可以解决 cross-thread 访问潜在问题,但是,因为排队的“工作”必须按顺序完成并与 COM 对象相关, ConcurrentQueue 目前看来是一个不错的解决方案。但是,稍后将不得不重新访问它。