how to create and update window in child thread using vc++ - multithreading

I just coded a child thread to create and update window but I am facing some problem. My window closes automatically after that thread execution is completed (naturally). But I don't want to close it so I tried putting a while loop in thread and in that loop I am calling InvalidateRect() function so that it can update window. Now window is not closing automatically but i can't move it or interact with it and cursor also showing some busy icon(means completely not responding). How I can solve that problem. below is code:
calling this from main()
bool CameraApp::OnInit()
{
hThread = (HANDLE)_beginthreadex( NULL, 0, &CameraFrame::StartCameraPreview,
NULL, 0, &threadID );
WaitForSingleObject( hThread, INFINITE );
CloseHandle( hThread );
return TRUE;
}
Thread function block
unsigned __stdcall CameraFrame::StartCameraPreview( void* pArgs )
{
cFrame.ShowCameraWindow();
while(1)
{
cFrame.StartCapture();
InvalidateRect(hwnd, NULL, false);
Sleep(5000);
}
_endthreadex( 0 );
return 0;
}
i can't use main() function to create window. So, i have to use thread and update that window with periodic image taken from web-camera.

Instead of your infinite loop you need to create message pump in secondary thread that processes windows messages.
unsigned __stdcall CameraFrame::StartCameraPreview( void* pArgs )
{
cFrame.ShowCameraWindow();
MSG msg;
while (GetMessage(&msg, NULL, 0, 0))
{
TranslateMessage(&msg);
DispatchMessage(&msg);
}
_endthreadex( 0 );
return 0;
}

Related

DoModal in critical section

in an parallel loop, there is a critical section. I try to execute an mfc dialog with DoModal in the critical section, however since main thread waits for parallel threads, there is no way for my dialog to show up and execute. In order to break this dependency, I create an executable and I run it as a process within my parallel loop. When the process shows dialog and gets the information. It returns and other threads keeps running.
However my team leader insist that there is a better way to do it which I couldn't figure out after doing hours of search :\
I tried a seperate thread in parallel for. It didn't worked.
I tried CWinThread (google say it is gui thread :\ which didn't helped)
I tired creating an exe and running it. That worked :)
int someCriticDialog()
{
#pragma omp critic (showCriticDlg)
{
CMyDialog ccc;
ccc.DoModal();
/* However the code below works
CreateProcess("someCriticDlg.exe", null, &hProcess);
WaitForSingeObject(hProcess, INFINITE);
*/
}
}
#pragma omp parallel
for (int i = 0; i < 5; i++)
someCriticDialog();
Let's say here is the problem:
void trouble_maker()
{
Sleep(10000);//application stops for 10 seconds
}
You can use PostMessage + PeekMessage + modal dialog to wait for it to finish through GUI window:
void PumpWaitingMessages()
{
MSG msg;
while (::PeekMessage(&msg, NULL, NULL, NULL, PM_NOREMOVE))
if (!AfxGetThread()->PumpMessage())
return;
}
BEGIN_MESSAGE_MAP(CMyDialog, CDialog)
ON_COMMAND(2000, OnDoSomething)
ON_COMMAND(IDCANCEL, OnCancel)
END_MESSAGE_MAP()
CMyDialog::CMyDialog(CWnd* par /*=NULL*/) : CDialog(IDD_DIALOG1, par)
{
working = false;
stop = false;
}
BOOL CMyDialog::OnInitDialog()
{
BOOL res = CDialog::OnInitDialog();
//call the function "OnDoSomething", but don't call it directly
PostMessage(WM_COMMAND, 2000, 0);
return res;
}
void CMyDialog::OnCancel()
{
if (working)
{
stop = true;
}
else
{
CDialog::OnCancel();
}
}
void CMyDialog::OnDoSomething()
{
HANDLE h = CreateThread(NULL, 0, (LPTHREAD_START_ROUTINE)&trouble_maker, NULL, 0, NULL);
working = true;
for (;;)
{
if (WAIT_TIMEOUT != WaitForSingleObject(h, 100)) break;
PumpWaitingMessages();
//update progress bar or something...
if (stop)
{
//terminate if it's safe
//BOOL res = TerminateThread(h, 0);
//CloseHandle(h);
//CDialog::OnCancel();
//return;
}
}
working = false;
MessageBox("done");
}

Terminating a Worker thread from a Parent thread - MFC

I've just started with learning MFC and I'm writing one dialog based application for better understanding of Multi-Threading.
The main dialog has a progress bar, a Start button and a Cancel button.
On click of the start button, i'm creating a worker thread to do some processing(through API call) and the main thread takes care of Progress bar.
I've defined a couple of Windows Messages to update and stop Progress bar status
WM_UPDATE_CONTROL
WM_STOP_CONTROL
Below is the code that i've created so far
HWND* phObjectHandle;
CWinThread* thread;
void CprogCtrlDlg::OnBnClickedStart() {
phObjectHandle = new HWND; // Set object handle for Worker thread
*phObjectHandle = GetSafeHwnd();
// create worker thread
if(NULL == (thread = AfxBeginThread(ThreadFunc, phObjectHandle))) {
EndDialog(IDCANCEL);
}
AfxMessageBox(L"Thread started");
// Set Progress bar to marquee
}
void CprogCtrlDlg::OnBnClickedCancel() {
// kill the Worker thread
}
UINT CprogCtrlDlg::ThreadFunc(LPVOID pParam) {
HWND *pObjectHandle = static_cast<HWND *>(pParam);
CprogCtrlImpDlg* threadDlg = (CprogCtrlImpDlg*) pParam;
return threadDlg->ThreadFuncRun(pObjectHandle);
}
UINT CprogCtrlDlg::ThreadFuncRun(HWND* pObjectHandle) {
::PostMessage(*pObjectHandle, WM_UPDATE_CONTROL, 0, 0);
// repetitive API CALL in a loop
::PostMessage(*pObjectHandle, WM_STOP_CONTROL, 0, 0);
AfxMessageBox(L"Thread completed");
return 0;
}
I want to terminate the Worker thread from a Parent thread, if a Cancel button is clicked.
I tried using TerminateThread()(though it wasn't a suggested one) but I couldn't kill the thread.
Please comment and share your thoughts on terminating a worker thread from a parent thread.
I'm using visual studio 2010 on Windows 7
TIA
I would amend your code something like this.
Have some member variables in your dialog class to hold the thread handle and an event handle (initialise to NULL in the constructor):
CWinThread* m_hThread;
HANDLE m_hKillEvent;
Use a static function as your thread entry point, pass the dialog this as the parameter, then delegate the call back to the class instance so you have access to all of the dialog's variables:
UINT ThreadFunc(LPVOID pParam)
{
// static thread func - delegate to instance
CprogCtrlDlg* pDlg = static_cast<CprogCtrlDlg*>(pParam);
return pDlg->ThreadFuncRun();
}
When you start the thread, create an event too:
void CprogCtrlDlg::OnBnClickedStart()
{
// create worker thread
m_hKillEvent = CreateEvent(NULL, FALSE, FALSE, NULL);
m_hThread = AfxBeginThread(ThreadFunc, this);
AfxMessageBox(L"Thread started");
}
To kill the thread, just set the event and wait on the thread handle, which will get signaled on death:
void CprogCtrlDlg::OnBnClickedCancel()
{
// kill the Worker thread
SetEvent(m_hKillEvent);
// wait for it to die
DWORD dwRet = WaitForSingleObject(m_hThread->m_hThread, 5000);
if (dwRet == WAIT_TIMEOUT)
{
// thread failed to die after 5 seconds
// error handling (maybe TerminateThread here)
}
}
In the thread function (now in the dialog class) you can post messages to yourself to indicate progress and use a wait on the event to catch a kill request:
UINT CprogCtrlDlg::ThreadFuncRun()
{
// instance thread func
PostMessage(WM_UPDATE_CONTROL, 0, 0);
// main loop
while (true)
{
// check kill
DWORD dwRet = WaitForSingleObject(m_hKillEvent, 0);
if (dwRet == WAIT_OBJECT_0) break;
// do a little work here and update progress
// ... so this is part of your working loop ...
PostMessage(WM_UPDATE_CONTROL, 0, 1 /*2,3,4,...*/);
}
// normal thread exit
PostMessage(WM_STOP_CONTROL, 0, 0);
return 0;
}
I've left out initialisation, cleanup of pointers, handles etc. but you get the general idea I hope.
There are several ways you can code the thread loop, you can do it like above where you periodically check to see if the event is signaled, or you can wait on the event to get signaled to do the work. Both are common patterns and often used together with two events - one for triggering work and the other for killing. See this answer for some important points to note if waiting on multiple events.
For a simple progress bar update, you can put the event check inside the work loop, something like this:
UINT CprogCtrlDlg::ThreadFuncRun()
{
// instance thread func
PostMessage(WM_UPDATE_CONTROL, 0, 0);
// main loop
for (int i = 0; i < 100; ++i)
{
// check kill
DWORD dwRet = WaitForSingleObject(m_hKillEvent, 0);
if (dwRet == WAIT_OBJECT_0) break;
// do a little work here and update progress
PostMessage(WM_UPDATE_CONTROL, 0, (LPARAM)i);
}
// normal thread exit
PostMessage(WM_STOP_CONTROL, 0, 0);
return 0;
}

Multithreaded program becomes nonresponsive on _multiple_ CPUs, but fine on a single CPU (when updating a ListView)

Update: I've reproduced the problem! Scroll lower to see the code.
Quick Notes
My Core i5 CPU has 2 cores, hyperthreading.
If I call SetProcessAffinityMask(GetCurrentProcess(), 1), everything is fine, even though the program is still multithreaded.
If I don't do that, and the program is running on Windows XP (it's fine on Windows 7 x64!), my GUI starts locking up for several seconds while I'm scrolling the list view and the icons are loading.
The Problem
Basically, when I run the program posted below (a reduced version of my original code) on Windows XP (Windows 7 is fine), unless I force the same logical CPU for all my threads, the program UI starts lagging behind by half a second or so.
(Note: Lots of edits to this post here, as I investigated the problem further.)
Note that the number of threads is the same -- only the affinity mask is different.
I've tried this out using two different methods of message-passing: the built-in GetMessage as well as my own BackgroundWorker.
The result? BackgroundWorker benefits from affinity for 1 logical CPU (virtually no lag), whereas GetMessage is completely hurt by this, (lag is now many seconds long).
I can't figure out why that would be happening -- shouldn't multiple CPUs work better than a single CPU?!
Why would there be such a lag, when the number of threads is the same?
More stats:
GetLogicalProcessorInformation returns:
0x0: {ProcessorMask=0x0000000000000003 Relationship=RelationProcessorCore ...}
0x1: {ProcessorMask=0x0000000000000003 Relationship=RelationCache ...}
0x2: {ProcessorMask=0x0000000000000003 Relationship=RelationCache ...}
0x3: {ProcessorMask=0x0000000000000003 Relationship=RelationCache ...}
0x4: {ProcessorMask=0x000000000000000f Relationship=RelationProcessorPackage ...}
0x5: {ProcessorMask=0x000000000000000c Relationship=RelationProcessorCore ...}
0x6: {ProcessorMask=0x000000000000000c Relationship=RelationCache ...}
0x7: {ProcessorMask=0x000000000000000c Relationship=RelationCache ...}
0x8: {ProcessorMask=0x000000000000000c Relationship=RelationCache ...}
0x9: {ProcessorMask=0x000000000000000f Relationship=RelationCache ...}
0xa: {ProcessorMask=0x000000000000000f Relationship=RelationNumaNode ...}
The Code
The code below should shows this problem on Windows XP SP3.
(At least, it does on my computer!)
Compare these two:
Run the program normally, then scroll. You should see lag.
Run the program with the affinity command-line argument, then scroll. It should be almost completely smooth.
Why would this happen?
#define _WIN32_WINNT 0x502
#include <tchar.h>
#include <Windows.h>
#include <CommCtrl.h>
#pragma comment(lib, "kernel32.lib")
#pragma comment(lib, "comctl32.lib")
#pragma comment(lib, "user32.lib")
LONGLONG startTick = 0;
LONGLONG QPC()
{ LARGE_INTEGER v; QueryPerformanceCounter(&v); return v.QuadPart; }
LONGLONG QPF()
{ LARGE_INTEGER v; QueryPerformanceFrequency(&v); return v.QuadPart; }
bool logging = false;
bool const useWindowMessaging = true; // GetMessage() or BackgroundWorker?
bool const autoScroll = false; // for testing
class BackgroundWorker
{
struct Thunk
{
virtual void operator()() = 0;
virtual ~Thunk() { }
};
class CSLock
{
CRITICAL_SECTION& cs;
public:
CSLock(CRITICAL_SECTION& criticalSection)
: cs(criticalSection)
{ EnterCriticalSection(&this->cs); }
~CSLock() { LeaveCriticalSection(&this->cs); }
};
template<typename T>
class ScopedPtr
{
T *p;
ScopedPtr(ScopedPtr const &) { }
ScopedPtr &operator =(ScopedPtr const &) { }
public:
ScopedPtr() : p(NULL) { }
explicit ScopedPtr(T *p) : p(p) { }
~ScopedPtr() { delete p; }
T *operator ->() { return p; }
T &operator *() { return *p; }
ScopedPtr &operator =(T *p)
{
if (this->p != NULL) { __debugbreak(); }
this->p = p;
return *this;
}
operator T *const &() { return this->p; }
};
Thunk **const todo;
size_t nToDo;
CRITICAL_SECTION criticalSection;
DWORD tid;
HANDLE hThread, hSemaphore;
volatile bool stop;
static size_t const MAX_TASKS = 1 << 18; // big enough for testing
static DWORD CALLBACK entry(void *arg)
{ return ((BackgroundWorker *)arg)->process(); }
public:
BackgroundWorker()
: nToDo(0), todo(new Thunk *[MAX_TASKS]), stop(false), tid(0),
hSemaphore(CreateSemaphore(NULL, 0, 1 << 30, NULL)),
hThread(CreateThread(NULL, 0, entry, this, CREATE_SUSPENDED, &tid))
{
InitializeCriticalSection(&this->criticalSection);
ResumeThread(this->hThread);
}
~BackgroundWorker()
{
// Clear all the tasks
this->stop = true;
this->clear();
LONG prev;
if (!ReleaseSemaphore(this->hSemaphore, 1, &prev) ||
WaitForSingleObject(this->hThread, INFINITE) != WAIT_OBJECT_0)
{ __debugbreak(); }
CloseHandle(this->hSemaphore);
CloseHandle(this->hThread);
DeleteCriticalSection(&this->criticalSection);
delete [] this->todo;
}
void clear()
{
CSLock lock(this->criticalSection);
while (this->nToDo > 0)
{
delete this->todo[--this->nToDo];
}
}
unsigned int process()
{
DWORD result;
while ((result = WaitForSingleObject(this->hSemaphore, INFINITE))
== WAIT_OBJECT_0)
{
if (this->stop) { result = ERROR_CANCELLED; break; }
ScopedPtr<Thunk> next;
{
CSLock lock(this->criticalSection);
if (this->nToDo > 0)
{
next = this->todo[--this->nToDo];
this->todo[this->nToDo] = NULL; // for debugging
}
}
if (next) { (*next)(); }
}
return result;
}
template<typename Func>
void add(Func const &func)
{
CSLock lock(this->criticalSection);
struct FThunk : public virtual Thunk
{
Func func;
FThunk(Func const &func) : func(func) { }
void operator()() { this->func(); }
};
DWORD exitCode;
if (GetExitCodeThread(this->hThread, &exitCode) &&
exitCode == STILL_ACTIVE)
{
if (this->nToDo >= MAX_TASKS) { __debugbreak(); /*too many*/ }
if (this->todo[this->nToDo] != NULL) { __debugbreak(); }
this->todo[this->nToDo++] = new FThunk(func);
LONG prev;
if (!ReleaseSemaphore(this->hSemaphore, 1, &prev))
{ __debugbreak(); }
}
else { __debugbreak(); }
}
};
LRESULT CALLBACK MyWindowProc(
HWND hWnd, UINT uMsg, WPARAM wParam, LPARAM lParam)
{
enum { IDC_LISTVIEW = 101 };
switch (uMsg)
{
case WM_CREATE:
{
RECT rc; GetClientRect(hWnd, &rc);
HWND const hWndListView = CreateWindowEx(
WS_EX_CLIENTEDGE, WC_LISTVIEW, NULL,
WS_CHILDWINDOW | WS_VISIBLE | LVS_REPORT |
LVS_SHOWSELALWAYS | LVS_SINGLESEL | WS_TABSTOP,
rc.left, rc.top, rc.right - rc.left, rc.bottom - rc.top,
hWnd, (HMENU)IDC_LISTVIEW, NULL, NULL);
int const cx = GetSystemMetrics(SM_CXSMICON),
cy = GetSystemMetrics(SM_CYSMICON);
HIMAGELIST const hImgList =
ImageList_Create(
GetSystemMetrics(SM_CXSMICON),
GetSystemMetrics(SM_CYSMICON),
ILC_COLOR32, 1024, 1024);
ImageList_AddIcon(hImgList, (HICON)LoadImage(
NULL, IDI_INFORMATION, IMAGE_ICON, cx, cy, LR_SHARED));
LVCOLUMN col = { LVCF_TEXT | LVCF_WIDTH, 0, 500, TEXT("Name") };
ListView_InsertColumn(hWndListView, 0, &col);
ListView_SetExtendedListViewStyle(hWndListView,
LVS_EX_DOUBLEBUFFER | LVS_EX_FULLROWSELECT | LVS_EX_GRIDLINES);
ListView_SetImageList(hWndListView, hImgList, LVSIL_SMALL);
for (int i = 0; i < (1 << 11); i++)
{
TCHAR text[128]; _stprintf(text, _T("Item %d"), i);
LVITEM item =
{
LVIF_IMAGE | LVIF_TEXT, i, 0, 0, 0,
text, 0, I_IMAGECALLBACK
};
ListView_InsertItem(hWndListView, &item);
}
if (autoScroll)
{
SetTimer(hWnd, 0, 1, NULL);
}
break;
}
case WM_TIMER:
{
HWND const hWndListView = GetDlgItem(hWnd, IDC_LISTVIEW);
RECT rc; GetClientRect(hWndListView, &rc);
if (!ListView_Scroll(hWndListView, 0, rc.bottom - rc.top))
{
KillTimer(hWnd, 0);
}
break;
}
case WM_NULL:
{
HWND const hWndListView = GetDlgItem(hWnd, IDC_LISTVIEW);
int const iItem = (int)lParam;
if (logging)
{
_tprintf(_T("#%I64lld ms:")
_T(" Received: #%d\n"),
(QPC() - startTick) * 1000 / QPF(), iItem);
}
int const iImage = 0;
LVITEM const item = {LVIF_IMAGE, iItem, 0, 0, 0, NULL, 0, iImage};
ListView_SetItem(hWndListView, &item);
ListView_Update(hWndListView, iItem);
break;
}
case WM_NOTIFY:
{
LPNMHDR const pNMHDR = (LPNMHDR)lParam;
switch (pNMHDR->code)
{
case LVN_GETDISPINFO:
{
NMLVDISPINFO *const pInfo = (NMLVDISPINFO *)lParam;
struct Callback
{
HWND hWnd;
int iItem;
void operator()()
{
if (logging)
{
_tprintf(_T("#%I64lld ms: Sent: #%d\n"),
(QPC() - startTick) * 1000 / QPF(),
iItem);
}
PostMessage(hWnd, WM_NULL, 0, iItem);
}
};
if (pInfo->item.iImage == I_IMAGECALLBACK)
{
if (useWindowMessaging)
{
DWORD const tid =
(DWORD)GetWindowLongPtr(hWnd, GWLP_USERDATA);
PostThreadMessage(
tid, WM_NULL, 0, pInfo->item.iItem);
}
else
{
Callback callback = { hWnd, pInfo->item.iItem };
if (logging)
{
_tprintf(_T("#%I64lld ms: Queued: #%d\n"),
(QPC() - startTick) * 1000 / QPF(),
pInfo->item.iItem);
}
((BackgroundWorker *)
GetWindowLongPtr(hWnd, GWLP_USERDATA))
->add(callback);
}
}
break;
}
}
break;
}
case WM_CLOSE:
{
PostQuitMessage(0);
break;
}
}
return DefWindowProc(hWnd, uMsg, wParam, lParam);
}
DWORD WINAPI BackgroundWorkerThread(LPVOID lpParameter)
{
HWND const hWnd = (HWND)lpParameter;
MSG msg;
while (GetMessage(&msg, NULL, 0, 0) > 0 && msg.message != WM_QUIT)
{
if (msg.message == WM_NULL)
{
PostMessage(hWnd, msg.message, msg.wParam, msg.lParam);
}
}
return 0;
}
int _tmain(int argc, LPTSTR argv[])
{
startTick = QPC();
bool const affinity = argc >= 2 && _tcsicmp(argv[1], _T("affinity")) == 0;
if (affinity)
{ SetProcessAffinityMask(GetCurrentProcess(), 1 << 0); }
bool const log = logging; // disable temporarily
logging = false;
WNDCLASS wndClass =
{
0, &MyWindowProc, 0, 0, NULL, NULL, LoadCursor(NULL, IDC_ARROW),
GetSysColorBrush(COLOR_3DFACE), NULL, TEXT("MyClass")
};
HWND const hWnd = CreateWindow(
MAKEINTATOM(RegisterClass(&wndClass)),
affinity ? TEXT("Window (1 CPU)") : TEXT("Window (All CPUs)"),
WS_OVERLAPPEDWINDOW | WS_VISIBLE, CW_USEDEFAULT, CW_USEDEFAULT,
CW_USEDEFAULT, CW_USEDEFAULT, NULL, NULL, NULL, NULL);
BackgroundWorker iconLoader;
DWORD tid = 0;
if (useWindowMessaging)
{
CreateThread(NULL, 0, &BackgroundWorkerThread, (LPVOID)hWnd, 0, &tid);
SetWindowLongPtr(hWnd, GWLP_USERDATA, tid);
}
else { SetWindowLongPtr(hWnd, GWLP_USERDATA, (LONG_PTR)&iconLoader); }
MSG msg;
while (GetMessage(&msg, NULL, 0, 0) > 0)
{
if (!IsDialogMessage(hWnd, &msg))
{
TranslateMessage(&msg);
DispatchMessage(&msg);
}
if (msg.message == WM_TIMER ||
!PeekMessage(&msg, NULL, 0, 0, PM_NOREMOVE))
{ logging = log; }
}
PostThreadMessage(tid, WM_QUIT, 0, 0);
return 0;
}
Based on the inter-thread timings you posted at http://ideone.com/fa2fM, it looks like there is a fairness issue at play here. Based solely on this assumption, here is my reasoning as to the apparent cause of the perceived lag and a potential solution to the problem.
It looks like there is a large number of LVN_GETDISPINFO messages being generated and processed on one thread by the window proc, and while the background worker thread is able to keep up and post messages back to the window at the same rate, the WM_NULL messages it posts are so far back in the queue that it takes time before they get handled.
When you set the processor affinity mask, you introduce more fairness into the system because the same processor must service both threads, which will limit the rate at which LVN_GETDISPINFO messages are generated relative to the non-affinity case. This means that the window proc message queue is likely not as deep when you post your WM_NULL messages, which in turn means that they will be processed 'sooner'.
It seems that you need to somehow bypass the queueing effect. Using SendMessage, SendMessageCallback or SendNotifyMessage instead of PostMessage may be ways to do this. In the SendMessage case, your worker thread will block until the window proc thread is finished its current message and processes the sent WM_NULL message, but you will be able to inject your WM_NULL messages more evenly into the message processing flow. See this page for an explanation of queued vs. non-queued message handling.
If you choose to use SendMessage, but you don't want to limit the rate at which you can obtain icons due to the blocking nature of SendMessage, then you can use a third thread. Your I/O thread would post messages to the third thread, while the third thread uses SendMessage to inject icon updates into the UI thread. In this fashion, you have control of the queue of satisfied icon requests, instead of interleaving them into the window proc message queue.
As for the difference in behaviour between Win7 and WinXP, there may be a number of reasons why you don't appear to see this effect on Win7. It could be that the list view common control is implemented differently and limits the rate at which LVN_GETDISPINFO messages are generated. Or perhaps the thread scheduling mechanism in Win7 switches thread contexts more frequently or more fairly.
EDIT:
Based on your latest change, try the following:
...
struct Callback
{
HWND hWnd;
int iItem;
void operator()()
{
if (logging)
{
_tprintf(_T("#%I64lld ms: Sent: #%d\n"),
(QPC() - startTick) * 1000 / QPF(),
iItem);
}
SendNotifyMessage(hWnd, WM_NULL, 0, iItem); // <----
}
};
...
DWORD WINAPI BackgroundWorkerThread(LPVOID lpParameter)
{
HWND const hWnd = (HWND)lpParameter;
MSG msg;
while (GetMessage(&msg, NULL, 0, 0) > 0 && msg.message != WM_QUIT)
{
if (msg.message == WM_NULL)
{
SendNotifyMessage(hWnd, msg.message, msg.wParam, msg.lParam); // <----
}
}
return 0;
}
EDIT 2:
After establishing that the LVN_GETDISPINFO message are being placed in the queue using SendMessage instead of PostMessage, we can't use SendMessage ourselves to bypass them.
Still proceeding on the assumption that there is a glut of messages being processed by the wndproc before the icon results are being sent back from the worker thread, we need another way to get those updates handled as soon as they are ready.
Here's the idea:
Worker thread places results in a synchronized queue-like data structure, and then posts (using PostMessage) a WM_NULL message to the wndproc (to ensure that the wndproc gets executed sometime in the future).
At the top of the wndproc (before the case statements), the UI thread checks the synchronized queue-like data structure to see if there are any results, and if so, removes one or more results from the queue-like data structure and processes them.
The issue has less to do with thread affinity and more to do with telling the listview that it needs to update the list item every time you update it. Because you do not add the LVIF_DI_SETITEM flag to pInfo->item.mask in your LVN_GETDISPINFO handler, and because you call ListView_Update manually, when you call ListView_Update, the list view invalidates any item that still has its iImage set to I_IMAGECALLBACK.
You can fix this in one of two ways (or a combination of both):
Remove ListView_Update from your WM_NULL handler. The list view will automatically redraw the items you set the image for in your WM_NULL handler when you set them, and it will not attempt to redraw items you haven't set the image for more than once.
Set LVIF_DI_SETITEM flag in pInfo->item.mask in your LVN_GETDISPINFO handler and set pInfo->item.iImage to a value that is not I_IMAGECALLBACK.
I repro'd similar awful behavior doing a full page scroll on Vista. Doing either of the above fixed the issue while still updating the icons asynchronously.
Its plausible to suggest that this is related to XPs hyper threading/logical core scheduling and I will second IvoTops suggestion to try this with hyper-threading disabled. Please try this and let us know.
Why? Because:
a) Logical cores offer bad parallelism for CPU bound tasks. Running multiple CPU bound threads on two logical HT cores on the same physical core is detrimental to performance. See for example, this intel paper - it explains how enabling HT might cause typical server threads to incur an increase in latency or processing time for each request (while improving net throughput.)
b) Windows 7 does indeed have some HT/SMT (symmetrical multi threading) scheduling improvements. Mark Russinovich's slides here mention this briefly. Although they claim that XP scheduler is SMT aware, the fact that Windows 7 explicitly fixes something around this, implies there could be something lacking in XP. So I'm guessing that the OS isn't setting the thread affinity to the second core appropriately. (perhaps because the second core might not be idle at the instant of scheduling your second thread, to speculate wildly).
You wrote "I just tried setting the CPU affinity of the process (or even the individual threads) to all potential combinations I could think of, on the same and on different logical CPUs".
Can we try to verify that the execution is actually on the second core, once you set this?
You can visually check this in task manager or perfmon/perf counters
Maybe post the code where you set the affinity of the threads (I note that you are not checking the return value on SetProcessorAffinity, do check that as well.)
If Windows perf counters dont help, Intel's VTune Performance Analyzer is helpful for exactly this kind of stuff.
I think you can force the thread affinity manually using task manager.
One more thing: Your core i5 is either Nehalem or SandyBridge micro-architecture. Nehalem and later HT implementation is significantly different from the prior generation architectures (Core,etc). In fact Microsoft recommended disabling HT for running Biztalk server on pre-Nehalem systems. So perhaps Windows XP does not handle the new HT architecture well.
This might be a hyperthreading bug. To check if that's what causing it run your faulty program with Hyperthreading turned off (in the bios you can usually switch it off). I have run into two issues in the last five years that only surfaced when hyperthreading was enabled.

Marquee in seperate thread in c++ builder 10

I have just found that making marquee in the same thread it's text get stopped a little in a time when my application loads data..
I am asking if anybody has done marquee functionality in their application in a seperate thread using TTimer.
Even in you do the marquee in a thread, you still have to synchronize it with the main thread for display, so you will still have the same problem if you continue doing lengthy data loads in the main thread. So do the data loading in a separate thread instead, and leave the marquee (and all other UI elements and logic) in the main thread, where it belongs. You should not be doing blocking operations in the main thread to begin with.
HANDLE hThread;
DWORD ThreadId;
int Data_Of_Thread_1 = 1;
unsigned long __stdcall ThreadFunc(void *Arg)
{
int a=0;
while(a != 100000000000000000)
{
a++;
Form1->ListBox1->Items->Add(a);
}
return 0;
}
void __fastcall TForm1::Button1Click(TObject *Sender)
{
// hThread=CreateThread(NULL,0,ThreadFunc,0,0,&ThreadId);
hThread = CreateThread( NULL, 0, ThreadFunc, &Data_Of_Thread_1, 0, &ThreadId);
if ( hThread == NULL)
{
ExitProcess(Data_Of_Thread_1);
}
}
void __fastcall TForm1::Button2Click(TObject *Sender)
{
TerminateThread(hThread,ThreadId);
}

Synchronising threads using Events in windows

I need to synchronize threads using events. First I am not getting concept of Events in windows. I will take an example, I have main thread and secondary thread. I created a two events called "write" and "test". In main thread I signaled for 'write" event and waited on the "test" event. Both the events are getting signaled.
Basically I am porting an appli from linux to windows. The linux program uses condition variable to signaling the thread. Condition variable is not available in window XP.
Ex:
#include <windows.h>
#include <stdio.h>
#define THREADCOUNT 1
HANDLE ghWriteEvent;
HANDLE ghtestEvent;
HANDLE ghThreads[THREADCOUNT];
DWORD WINAPI ThreadProc(LPVOID);
void CreateEventsAndThreads(void)
{
int i;
DWORD dwThreadID;
ghWriteEvent = CreateEvent(
NULL, // default security attributes
TRUE, // manual-reset event
FALSE, // initial state is nonsignaled
TEXT("WriteEvent") // object name
);
if (ghWriteEvent == NULL)
{
printf("CreateEvent failed (%d)\n", GetLastError());
return;
}
ghtestEvent = CreateEvent(
NULL, // default security attributes
FALSE, // manual-reset event
FALSE, // initial state is nonsignaled
TEXT("WriteEvent") // object name
);
if (ghtestEvent == NULL)
{
printf("CreateEvent failed (%d)\n", GetLastError());
return;
}
// Create multiple threads to read from the buffer.
for(i = 0; i < THREADCOUNT; i++)
{
// TODO: More complex scenarios may require use of a parameter
// to the thread procedure, such as an event per thread to
// be used for synchronization.
ghThreads[i] = CreateThread(
NULL, // default security
0, // default stack size
ThreadProc, // name of the thread function
NULL, // no thread parameters
0, // default startup flags
&dwThreadID);
if (ghThreads[i] == NULL)
{
printf("CreateThread failed (%d)\n", GetLastError());
return;
}
}
}
void WriteToBuffer(VOID)
{
DWORD dwWaitResult;
printf("Main thread writing to the shared buffer...\n");
printf("Posting Events for %d\n",ghWriteEvent );
// Set ghWriteEvent to signaled
if (! SetEvent(ghWriteEvent) )
{
printf("SetEvent failed (%d)\n", GetLastError());
return;
}
dwWaitResult= WaitForSingleObject(
ghtestEvent, // event handle
INFINITE); // indefinite wait
printf("WaitForSingleObject signelled (%d)\n", GetLastError());
if ( dwWaitResult == WAIT_OBJECT_0)
printf("Signlled State for %d with ret val : %d\n",ghtestEvent,dwWaitResult );
}
void CloseEvents()
{
// Close all event handles (currently, only one global handle).
CloseHandle(ghWriteEvent);
}
int main( void )
{
DWORD dwWaitResult;
CreateEventsAndThreads();
WriteToBuffer();
printf("Main thread waiting for threads to exit...\n");
// The handle for each thread is signaled when the thread is
// terminated.
dwWaitResult = WaitForMultipleObjects(
THREADCOUNT, // number of handles in array
ghThreads, // array of thread handles
TRUE, // wait until all are signaled
INFINITE);
switch (dwWaitResult)
{
// All thread objects were signaled
case WAIT_OBJECT_0:
printf("All threads ended, cleaning up for application exit...\n");
break;
// An error occurred
default:
printf("WaitForMultipleObjects failed (%d)\n", GetLastError());
return 1;
}
// Close the events to clean up
CloseEvents();
return 0;
}
DWORD WINAPI ThreadProc(LPVOID lpParam)
{
// lpParam not used in this example.
UNREFERENCED_PARAMETER(lpParam);
DWORD dwWaitResult;
printf("Thread %d waiting for write event...\n", GetCurrentThreadId());
dwWaitResult = WaitForSingleObject(
ghWriteEvent, // event handle
INFINITE); // indefinite wait
printf("rcvd event for Write is %d \n", ghWriteEvent);
switch (dwWaitResult)
{
// Event object was signaled
case WAIT_OBJECT_0:
WaitForSingleObject(
ghtestEvent, // event handle
INFINITE); // indefinite wait
printf("rcvd event for %d with Error %d\n", ghtestEvent,GetLastError());
if ( dwWaitResult == WAIT_OBJECT_0)
{
printf("Test event signaled for second thread \n");
}
break;
// An error occurred
default:
printf("Wait error (%d)\n", GetLastError());
printf("Wait error (%d)\n", GetLastError());
return 0;
}
printf("Thread %d exiting\n", GetCurrentThreadId());
return 1;
}
Output of the program is:
Main thread writing to the shared buffer...
Posting Events for 2012
WaitForSingleObject signelled (183)
Signlled State for 2008 with ret val : 0
Main thread waiting for threads to exit...
Thread 2016 waiting for write event...
rcvd event for Write is 2012
rcvd event for 2008 with Error 0
Test event signaled for second thread
Thread 2016 exiting
All threads ended, cleaning up for application exit...
After a long struggle I found out the reason. If Two events are created with same Name, the second event while creating will throw an Error "ERROR_ALREADY_EXISTS (183).
If event are created with different name, it works without any issue. It is good programming to check the error and proceed accordingly.
ghtestEvent = CreateEvent(
NULL, // default security attributes
FALSE, // manual-reset event
FALSE, // initial state is nonsignaled
TEXT("WriteEvent") // object name
);
if ( ERROR_ALREADY_EXISTS == GetLastError() )
{
printf("unable to create event (%d)\n".GetLastError());
exit(0);
}
Regards
JOhnnie
You only need to name events if you're going to use them to communicate between processes. You can pass NULL as the name if you're only synchronization threads within a single process.
When I'm creating named synchronization objects for inter-process communication, I generally give them a descriptive name (like "MyCompany - MyApp - WriteEvent") and tack on a GUID for good measure. You don't want to have a name conflict with some other developer who named his event "WriteEvent". The name can be up to MAX_PATH characters long and there is no performance penalty.

Resources