搜索/筛选功能会阻塞主(GUI)线程



我有一个自定义表,我实现了一个搜索/筛选功能,该功能会遍历表中的所有元素,然后根据该项目/元素是否与我们正在搜索的项目匹配来隐藏/显示表中的项目。例如,假设我有一个文本控件,并且我键入";wxwidgets";在它中,我的自定义函数将遍历表中的所有元素,并隐藏与此"不匹配的元素;wxwidgets";进入这很好用,我可以正确地隐藏/显示元素。但问题是,这个搜索阻塞了主线程(gui),因为我是在主线程中进行搜索的。该表有大约1000个条目,将来可能会更多。我的问题是如何避免主线程被阻塞。我正在考虑使用另一个工作线程来搜索元素。但后来我读到";任何辅助线程都不应该调用gui函数;。但是,我如何向工作线程显示/隐藏表的元素呢。例如,当前在主线程中,我使用Show(true)或Show(false)来显示/隐藏表中的特定条目,所有这些都是在For/while循环中完成的。但是,如果我在一个工作线程中实现了这个(for循环),那么根据引用的建议,我不应该在该工作线程内部使用Show()函数。在这种情况下该怎么办?此外,是否有其他方法/建议可以搜索表中的元素。每当用户在文本字段中输入一些文本时,我都会考虑启动一个新的可拆卸线程。然后,如果用户在文本字段中添加更多文本,并启动一个从头开始搜索的新线程,则删除该旧线程。这是正确的解决方案吗?

问题是在for循环中,我使用的是wxWidgets函数。例如,这就是我的For循环的样子:

void AnotherClass::onTextChanged(wxCommandEvent &event)
{
for(int i = 5; i<154;++i)
{                                                                                                                        
SomeClass *element = dynamic_cast<SomeClass*>(FindWindowById(i));
if(element.GetLabel() == textEnteredInTextCtrl)                                          
{                                                                                                                    
element.Show(true);//element found                                                        
//update the necessary layout here using layout call                                
}                                                                                                                    
else                                                                                                               
{                                                                                                                     
element.Show(false);//element not found                                                  
//update the necessary layout here using Layout() call                             
}                                                                                                                     
}                                                                                                                        
}   

这是搜索的主要部分。现在在workersread内部,我应该/可以使用FindWindowById()和GetLabel()这样的函数吗?它们是否被视为GUI函数,以便我可以从工作线程使用它们?我可以不可以在工作线程内部使用FindWindowByID()和GetLabel()以及其他类似的函数(如Layout()和Show())。我应该如何做到这一点?我的意思是,我知道如何使用wxThread并使用QueueEvent发送事件,并且我的程序中已经有另一个工作线程可以进行其他计算,但我正在询问在这种特殊情况下应该如何使其工作。

QuentinC提出的另一个解决方案是使用计时器。他的建议如下:

在我的情况下,我不会在用户在搜索框中键入了一封信。相反,当用户键入一个字母(wxEVT_TEXT),我启动或重新启动500毫秒的计时器。只有当计时器超时(用户停止键入500毫秒)时,列表被刷新。同样,这是为了避免无用的刷新。

但是在使用计时器的情况下,我有几个查询。我正在将wxEVT_TEXT从CustomTextCtrl的onTextChanged方法发送到此类的onTextChange方法。我想当用户键入一个字母时,我可以在CustomTextCtrl类的onTextChanged方法中启动500ms的计时器。但是,我应该在哪里检查计时器是否仍在运行?在CustomTextCtrl的onTextChanged方法中还是在AnotherClass的onTextChanged方法中?因此,对于澄清,我有两个类

  1. CustomTextCtrl类,该类具有使用event的onTextChanged方法。Skip()将此事件转发给其父级
  2. 父类AnotherClass也有一个onTextChanged方法,该方法接收这个转发的事件并进行搜索和更新表

我应该在哪里以及如何启动/重新启动/停止计时器来更新UI?

注意:过滤元素的过程运行良好,但唯一的问题是,当用户在textctrl中键入一些文本时,主(GUI)线程会被阻塞。比方说6或7秒后,文本出现在textctrl中,UI也随之更新。我不希望主UI在6-7秒内没有响应。

另外,请注意,我没有使用任何wxList/wxGrid。我只是使用wxPanel和wxStaticText,并在它们上使用显示/隐藏。

编辑:上面代码中的一个改进是只使用来自for循环外部的Layout()调用。如果我在for循环之外使用Layout()调用,那么搜索功能几乎可以立即工作。但是,如果表中有更多的元素,这个(方法)在将来仍然有可能阻塞主线程。所以我想使用线程或定时器方法。但我不知道辅助线程如何使用gui函数,也不知道我如何/应该使用wxTimer方法(如果有的话)来解决这个问题。

我对这个子喷气有几个想法

A) 如果你有速度问题,那么你最好分析一下你的代码,看看瓶颈在哪里。

B) 从非主线程调用GDI函数是有风险的。也许仅仅询问窗口id及其标签并没有那么危险,但我认为调用Show()肯定是危险的。

C) 这段代码主要与GUI相关。我不认为工作线程在这里有用。但堆叠类似的愈伤组织可能会提高其速度。对此,我有三个建议:

    1. 使用CallAfter()传递elemnt.Show()方法
    1. 在循环之前使用Freeze(),然后解冻
    1. 仅在循环后调用Layout()一次。关于这一点,我想知道Show()/Hide()控件是否比Enable()/Disabñe()更好

D)因为调用FindWindowById()的次数太多,而且用户更改也很多,所以最好将所有受影响的窗口缓存在一个容器中(以id为密钥的std::map)。然后,在循环中使用容器,而不是"FindWindowById()"。

E) 作为最后一个资源,如果GUI仍然被阻塞,则每xxx(比如100)次循环迭代使用wxYield()。根据挂起的消息,此解决方案可能会使情况变得更糟(重入、交叉效应等)。

最新更新