【发布时间】:2020-10-20 00:09:47
【问题描述】:
我有一个专用类,其中包括一个 BackgroundWorker,它负责从队列中运行特定于类的操作 - 需要使用 COM 对象的操作。
在应用程序启动 (WPF) 时从 UI 线程在运行时创建专用类的对象。当调用该类的构造函数时,它会实例化一个 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(); 行确实会在 WPF 应用程序中的任何用户输入再次处理之前导致 10 多秒。
如何隔离,使与 COM 对象相关的功能不会影响 UI 线程的性能?
(注意:BackgroundWorker 排队的操作与 UI 线程没有交互。在这些操作中仅更改特定于类的属性)。
【问题讨论】:
-
为了记录,您不仅要直接在主线程上执行 COM 操作(
new COM()发生在 UI 线程上),而且您应该查找您最有可能放置的STAThread属性你的主要功能。另外,不要像在线程之间那样使用Queue<>,你是在要求崩溃。 -
下面是阻塞用户代码:this.COMObject = new COM();您可以将这行代码移至 WorkerCompleted 事件。 workerComp[leted 事件不必在 BackGroundWorker 中。所以我会移动到外部,以便其余代码可以访问数据。
-
@Blindy - Queue
包含特定于线程的操作(与任何其他线程的函数无关),这还会带来风险吗? -
不管它持有什么,你在 UI 线程中排队并在后台工作线程中排队,这显然是灾难的根源。
-
使用 ConcurrentQueue 代替队列
标签: c# wpf multithreading com backgroundworker