问题描述
我是一名爱好程序员,学习C ++和多线程,并开始我的第一次线程池尝试。 。 我到了校长正在工作的地步。
我想要实现的是从音乐文件(FLAC)中提取20个标签。一个会话中可能要扫描7000个文件。
每次提取是在具有16个线程的线程池中执行的单独活动, 并将最终结果(未来)推入结构的向量中以供以后处理。
线程池代码是借来的:
https://codereview.stackexchange.com/questions/221626/c17-thread-pool
我在Windows 10 Pro计算机上使用Code :: Blocks 20.3,wxWidgets 3.1.3和MinGW 17.1。
我现在面临的问题是高级磁盘访问阻止了应用刷新窗口界面。 窗口显示臭名昭著的(“无响应”)消息。
我的应用程序包含一个主Frame类和一个Panel类。被调用的函数是“自由函数”。 尝试“强制”更新窗口的代码是:Refresh()和Update()。
已采取的措施:
每200毫秒触发一次事件的wxTimer。
一个带有while循环的单独线程,该线程休眠200ms,并且在线程处理结束时可以通过原子布尔停止。
最后但并非最不重要,但仍然无效
wxStopWatch swt;
for (auto &Fut : Futures)
{
TagsStruct TLf = TagsStruct();
TLf = Fut.get();
vTrackTags.push_back(TLf);
if (swt.Time() > 200)
{
// ToDo: code for updating one progress bar
m_wnd->Refresh(); // m_wnd is a pointer passed from the Panel Class
m_wnd->Update();
swt.Start();
}
}
秒表计时不一致(从平均200毫秒到370毫秒不等),但足以更新进度条。
必须有一种机制来腾出时间来更新窗口。 我购买了一个可转换文件的应用程序。 有时需要15分钟才能执行,并且保持16个进度条保持活动状态并没有问题。 因此,原则上,线程运行时应该可以更新进度条。
希望有人可以帮助我解决这个问题。
粗鲁
通过按钮事件添加了代码:
void FetchTags::m_btn_Fetch_OnButtonClick( wxCommandEvent& event )
{
// Set Collection Name
wxString wsCollection{m_textCtrl1->GetLineText(0)},wsCol{"Empty"};
if (wsCollection != "") { wsCol = wsCollection; };
// Set number of threads
int t_cnt = m_spinCtrl1->GetValue();
if (wsTrackFiles.size() > 0)
{
Elements(false);
auto TrackTags = ExtractMultiTags(t_cnt,wsTrackFiles,wsCol,this); // though thread-pool
Elements(true);
if (TrackTags.size() > 0)
{
// Grid is cleared in OnDropFiles()
m_grid1->AppendRows(TrackTags.size());
FillGrid(TrackTags);
WriteToCSV(TrackTags);
}
std::cout << "i_cnt = " << i_cnt << std::endl;
}
}
//-
std::vector<TagsStruct> ExtractMultiTags(int th_cnt,std::vector<wxString> vwsFiles,wxString wsCol,wxWindow *m_wnd)
{
wxStopWatch swf;
// Load the TagsLibrary DLL
if (!InitTagsLibrary())
{
//* Could not load the .dll
wxString msg = "\tError while loading TagsLib.dll\n";
wxMessageBox(msg,_("ERROR..."));
}
// Clear existing Vector of Futures
vTrackTags.clear();
// Create Thread Pool
Thread_Pool Pool(th_cnt);
std::vector<std::future<TagsStruct>> Futures;
// Do the work
for(auto &aTrack : vwsFiles)
{
TagsStruct TLp = TagsStruct();
Futures.push_back(Pool.execute(ExtractTrackTags,TLp,aTrack,wsCol));
}
// Get the results
for (auto &Fut : Futures)
{
TagsStruct TLf = TagsStruct();
TLf = Fut.get();
vTrackTags.push_back(TLf);
if (swt.Time() > 200)
{
// ToDo: code for updating one progress bar
m_wnd->Refresh();
m_wnd->Update();
swt.Start();
}
}
// Unload the .dll
FreeTagsLibrary();
return vTrackTags;
}
---从跟踪文件中提取---
static TagsStruct ExtractTrackTags(TagsStruct TagLine,wxString wsFile,wxString wsCollection)
{
// Convert std::string to LPWSTR
LPWSTR wsFileName{ConvertString(wsFile)};
// Load the tags
TagsLibrary_Load(Tags,wsFileName,ttAutomatic,TRUE);
if (TagsLibrary_Loaded(Tags,ttAutomatic))
{
/* Extract the Audio Attributes */
TAudioAttributes Attribs;
if (!TagsLibrary_GetAudioAttributes(Tags,TAudioType::atAutomatic,&Attribs))
{
TagLine.PlayTime = std::__cxx11::to_string(Attribs.PlayTime);
// etc...
}
/* Extract the named TAGs*/
//AlbumArtist
std::wstring ws05(TagsLibrary_GetTag(Tags,ConvertString("ALBUMARTIST"),ttAutomatic));
TagLine.AlbumArtist << std::string(ws05.begin(),ws05.end());
// etc...
}
else
{
TagLine.OK = false;
wxString msg = "\tNo tags found in:\n" + wsFile ;
wxMessageBox(msg,_("ERROR..."));
}
return TagLine;
}
下面是带有断点的调用堆栈
“ Futures.push_back(Pool.execute(ExtractTrackTags,TLp,aTrack,wsCol));”
#0 ?? ExtractMultiTags (th_cnt=th_cnt@entry=16,vwsFiles=...,wsCol=...,m_wnd=m_wnd@entry=0x1676f30) (F:\Data\__C++\wxApps\Mtags\TrackTags.cpp:167)
#1 0x402e12 FetchTags::m_btn_Fetch_OnButtonClick(this=0x1676f30,event=...) (f:/sdks/mingw-17.1/include/c++/9.2.0/bits/basic_string.h:263)
#2 0x417d68 wxAppConsoleBase::CallEventHandler(wxEvtHandler*,wxEventFunctor&,wxEvent&) const() (F:\Data\__C++\wxApps\Mtags\TrackTags.cpp:190)
#3 0x507c91 wxEvtHandler::ProcessEventIfMatchesId(wxEventTableEntryBase const&,wxEvtHandler*,wxEvent&) () (F:\Data\__C++\wxApps\Mtags\TrackTags.cpp:190)
#4 0x508137 wxEvtHandler::SearchDynamicEventTable(wxEvent&) () (F:\Data\__C++\wxApps\Mtags\TrackTags.cpp:190)
#5 0x5084a5 wxEvtHandler::TryHereOnly(wxEvent&) () (F:\Data\__C++\wxApps\Mtags\TrackTags.cpp:190)
#6 0x50853b wxEvtHandler::ProcessEventLocally(wxEvent&) () (F:\Data\__C++\wxApps\Mtags\TrackTags.cpp:190)
#7 0x508622 wxEvtHandler::ProcessEvent(wxEvent&) () (F:\Data\__C++\wxApps\Mtags\TrackTags.cpp:190)
#8 0x50a71c wxEvtHandler::SafelyProcessEvent(wxEvent&) () (F:\Data\__C++\wxApps\Mtags\TrackTags.cpp:190)
#9 0x57c373 wxButton::SendClickEvent() () (F:\Data\__C++\wxApps\Mtags\TrackTags.cpp:190)
#10 0x56095f wxWindow::HandleCommand(unsigned short,unsigned short,HWND__*) () (F:\Data\__C++\wxApps\Mtags\TrackTags.cpp:190)
#11 0x56bcaf wxWindow::MSWHandleMessage(long long*,unsigned int,unsigned long long,long long) () (F:\Data\__C++\wxApps\Mtags\TrackTags.cpp:190)
#12 0x55988f wxWindow::MSWWindowProc(unsigned int,long long) () (F:\Data\__C++\wxApps\Mtags\TrackTags.cpp:190)
#13 0x7ffeebf05c7d ?? () (??:??)
#0 ?? std::unique_lock<std::mutex>::unique_lock (__m=...,this=0x162dc80) (f:/sdks/mingw-17.1/include/c++/9.2.0/bits/move.h:47)
#1 ?? Thread_Pool::execute<TagsStruct (*)(TagsStruct,wxString,wxString),TagsStruct&,wxString&,wxString&> (this=this@entry=0x162e6e0,function=function@entry=0x411612 <ExtractTrackTags(TagsStruct,wxString)>,args#0=...,args#1=...,args#2=...) (F:/Data/__C++/wxApps/Mtags/Threadpool.h:62)
#2 0x416ddb ExtractMultiTags(th_cnt=th_cnt@entry=16,m_wnd=m_wnd@entry=0x1676f30) (F:\Data\__C++\wxApps\Mtags\TrackTags.cpp:167)
#3 0x402e12 FetchTags::m_btn_Fetch_OnButtonClick(this=0x1676f30,event=...) (f:/sdks/mingw-17.1/include/c++/9.2.0/bits/basic_string.h:263)
解决方法
假设您的期货总能得到解决,那么您可以在刷新UI直至准备就绪之前,在短时间内启用每个期货:
for (auto &Fut : Futures)
{
while (true) {
auto status = Fut.wait_for(100ms);
if (status == std::future_status::ready) break;
m_wnd->Refresh(); // Assuming these functions actually run the event loop
m_wnd->Update();
}
TagsStruct TLf = Fut.get();
vTrackTags.push_back(TLf);
}
或者,您可以保留阻塞循环,但在另一个线程中进行阻塞,并在WxEvent准备就绪时将其发送回UI线程,例如在WxThreadHelper中。