Make Duilib multi-threaded programming easier, duilib multi-threaded programming
1. Duilib cannot develop multi-threaded programs?
I remember a long time ago I heard someone say that Duilib's multi-thread support is poor because the controls in Duilib are global variables managed by arrays and cannot be accessed by multiple threads. Locking is troublesome. In fact, this statement is so unreasonable that Duilib is directly rejected when developing multi-threaded programs. Of course, using Duilib to develop multithreading is a problem. Do not simply think that other interface libraries can use multiple threads to operate a control at the same time. Duilib won't work. Facts have proved that MFC cannot do this, And WinForm cannot do anything that Microsoft cannot do. It cannot be considered a defect of Duilib.
Ii. UI thread and working thread
Anyone who has developed a Windows interface should know that the first step is to make a stable and non-flashing interface, and all the code to be drawn must be executed in sequence, in addition, the drawing code should be written in a single position, WM_PAINT (of course, it is okay to write it outside, but it also needs to be erased. this is generally not the case ). Therefore, all painting operations are performed asynchronously. To draw a project, you must first save it and use Invalidate () to asynchronously trigger WM_PAINT. When the WM_PAINT message is processed, draw the project again. This makes it impossible to draw a multi-thread UI. Therefore, all the painting operations are completed by the main thread where the message loop is located. Since multithreading cannot be used for plotting, how about the attributes of multiple threads simultaneously accessing the same control? (For example, there is A button A, one thread executes A-> SetWith (600), and the other thread executes A-> SetWith (800);). This is also A bad idea, the width attribute of the control is marked by an int value. The SetWith operation does not use atomic locks or any mutex lock, and access may fail. You may have done this before and have not found any problems. It is because the two threads are not likely to "run". You didn't run into it, but don't be lucky, A low probability does not mean no. An error may occur once it appears. To support multi-threaded access to control attributes, either each operation uses an atomic operator or each method is added with a mutex lock. However, the efficiency is too low. So what should I do when the worker thread needs to modify the control attributes? Likewise, asynchronous methods are used to post custom messages in the thread, and then process messages sent from other threads in the window function to callback the operations to be processed. Why is it asynchronous? This is because we use Windows's "system message queue" to act as our asynchronous event processing queue, whether it is a "Parallel" event or "serial" event, after the Message Queue orchestration, it will be processed in a serial mode, turning multithreading into a single thread.
For example, the download completion notification is called by the download thread. When we want to modify the control properties, we need to post a custom message.
void CMainFrame::OnFinished(int percent,TASK*){PostMessage(WM_UPDATEAD,0,0);}
Operate controls in the window processing functions
LRESULT CMainFrame::HandleMessage(UINT uMsg, WPARAM wParam, LPARAM lParam){if (uMsg == WM_UPDATEAD){CDuiString htmlPath = L"file:///";CDuiString currentPath = m_PaintManager.GetCurrentPath();htmlPath.Append(currentPath);htmlPath.Append(L"/html/Advertisement/ad.html");m_pWkeWeb->LoadUrl(L"/html/Advertisement/ad.html");}
However, writing like this is a headache. It is annoying for the compiler to get a custom message for every function.
Iii. skillcore, an application framework compiled by Skilla for Duilib, can be perfectly qualified for Multithreading
Although Duilib draws controls, it takes a lot of effort. However, in terms of application management, we are still using the original WinMain in the Age of "knife farming. Although this method is transparent to users, it is really difficult to deal with multi-threaded programs with less attention. Simply an inverted structure order can cause program crashes. Therefore, Skilla writes a set for Duilib following the Juce application framework to solve this problem (of course it is not as complicated as in Juce). Let's look at how to use it.
class TestApp : public DuiApplicationBase{public:void OnInit();void RequestQuit();void OnQuit();ScopedPtr<CMainFrame> m_pFrame;};START_DUI_APPLICATION(TestApp)void TestApp::OnInit(){wkeInit();CPaintManagerUI::SetInstance(*this);CPaintManagerUI::SetResourcePath(CPaintManagerUI::GetInstancePath());m_pFrame = new CMainFrame;if (m_pFrame == NULL) return ;m_pFrame->Create(NULL, _T("DuilibDemo"), UI_WNDSTYLE_FRAME, WS_EX_STATICEDGE | WS_EX_APPWINDOW, 0, 0, 800, 600);m_pFrame->CenterWindow();::ShowWindow(*m_pFrame, SW_SHOW);}void TestApp::RequestQuit(){wkeShutdown();Quit();} void TestApp::OnQuit(){}
How about it, isn't it easy? DuiApplicationBase encapsulates a message loop to process multi-threaded events. Of course, Duilib cannot be used in multi-thread programming. Skillcore also encapsulates threads and message loops. It also encapsulates memory and pointers to simplify complicated memory management during multi-threaded programming, the source code mainly draws on the boost library and Juce library, as well as files, input and output streams, atomic locks, mutex locks, and other auxiliary things.
Just now, it is very troublesome to call functions asynchronously to make custom messages. To solve this problem, skillcore provides a MsgPump class, which can be used to directly call functions using the main thread in other threads. Next, let's take a look at how to use it. The following code is a thread animation of a "convergence" action before the window is closed, similar to the effect of cool dog when it is closed.
class WindowDestroyThread : public Thread{public:WindowDestroyThread():Thread(L"DestroyThread"){}~WindowDestroyThread(){Stop(4000);}void run(){while (!IsShouldExit()){if (MsgPump::GetInstance()->IsShouldStop()) break;B_Function<int(WindowDestroyThread*)> f = B_Bind(WindowDestroyAnimation,_1);if (!MsgPump::GetInstance()->CallFun(f,this)){static_cast<TestApp*>(DuiApplicationBase::GetInstance())->RequestQuit();}Sleep(10);}}LEAKED_THE_CLASS(WindowDestroyThread)};int WindowDestroyAnimation(WindowDestroyThread* t){TestApp * app = static_cast<TestApp*>(DuiApplicationBase::GetInstance());CMainFrame* p = app->m_pFrame;RECT rect;::GetWindowRect(*p,&rect);CDuiRect r(rect);if (r.GetHeight()>100){MoveWindow(*p,rect.left,rect.top+15,r.GetWidth(),r.GetHeight()-30,false);return 1;}elsereturn 0;}void CMainFrame::Notify(TNotifyUI& msg){if (_tcsicmp(msg.sType,_T("windowinit"))==0){MoveWindow(*this,0,0,900,100,false);CenterWindow();initThread = new WindowInitThread;initThread->Start();CWkeBrowserUI* pBrowser = (CWkeBrowserUI*)m_PaintManager.FindControl(L"wke");pBrowser->LoadUrl(L"www.baidu.com");}else if (_tcsicmp(msg.sType,_T("click")) == 0){if (_tcsicmp(msg.pSender->GetName(),_T("btn_close")) == 0){destroyThread = new WindowDestroyThread();destroyThread->Start();}}}
When using this function, bind the function to be called to the B _Funtion (boost: function) object, and then use the CallFun function OF THE MsgPump singleton object to call it, the first parameter is the bound B _Function object, followed by the parameters of the called function. Currently, a maximum of four parameters are supported. If you need more parameters, you can expand them by yourself. Because each parameter added requires a piece of repetitive code, Skilla has already written four parameters, I really don't want to write any more.
There are also a bunch of smart pointers encapsulated in it. I believe that you should know how to use them without talking about them. ScopePtr self-destruction type, SharedPtr No root strong reference type, WeakPtr No root weak reference type, javasdobjectptr has a root strong reference type, ComSmartPtr calls the smart pointer used by the Com interface.
Threads also encapsulates locks such as critical zones, spin locks, and events that are commonly used in multithreading, making it easier to use.
After talking so much about it, I believe everyone is already at ease, and there is a feeling of eagerness. Don't worry. Skilla has contributed the source code, with a Demo of window animation. The link address is below. You can also easily implement cool thread animation.
Download Links of skillcore and test Demo (the CWkeBrowser control in it also uses threads to refresh, Which is smoother than the previous timer)
Due to the time relationship, code building is too hasty, and the library itself may still have bugs and deficiencies. If you find out, please contact Skilla (QQ: 848861075). Thank you!